在类<t> </t>的类型T的成员变量上调用扩展方法

时间:2013-11-14 08:24:29

标签: c# generics extension-methods

给出下面的扩展方法:

public static class Ext
{
    public static void Serialize(this Guid guid_, StringWriter sw_)
    {
        sw_.Write(guid_.ToString("B"));
    }
}

和班级:

public class Field<T>
{
    public T value;

    public void Serialize(StringWriter sw_)
    {
        value.Serialize(sw_);
    }
}

我想做以下事情,但无法弄清楚:

public class Foo
{
    public Field<Guid> uid;

    public Foo()
    {
        // assume StringWriter object sw;
        uid.Serialize(sw);
    }
}

显然现实情况更复杂,这只是一个简单的例子。

修改

编译器错误:

错误CS1928:'T'不包含'Serialize'的定义,并且最好的扩展方法重载'Ext.Serialize(System.Guid,StringWriter)'有一些无效的参数

错误CS1929:实例参数:无法从'T'转换为'System.Guid'

4 个答案:

答案 0 :(得分:1)

这是不可能的,因为在编译时肯定不知道T是否为Guid

但你可以做这样的事情

public class GuidField : Field<Guid>
{
    public override void Serialize(StringWriter sw_)//Assume we have made base class method virtual
    {
        value.Serialize(sw_);
    }
}

这是有效的,因为c#编译器在编译时知道valueGuid

<小时/> 以下方式将起作用,但它将击败“泛型”的点。不推荐。 为了说明该怎么做,我举一个例子

public void Serialize(StringWriter sw_)
{
    if (typeof (T) == typeof (Guid))
    {
        ((Guid)(object)value).Serialize(sw_);
    }
}

答案 1 :(得分:1)

无法应用限制,以使“T仅为具有为”。“定义的扩展方法的类型。

如果你真的需要为此使用泛型,你必须为每个可以序列化的类型创建一个适配器,如下:

public static class Ext
{
    public static void Serialize(this Guid guid_, StringWriter sw_)
    {
        sw_.Write(guid_.ToString("B"));
    }
}

public class Field<T> where T: ISerializableField
{
    public T value;

    public void Serialize(StringWriter sw_)
    {
        value.Serialize(sw_);
    }
}

public interface ISerializableField
{
    void Serialize(StringWriter sw);
}

public class SerializableGuid : ISerializableField
{
    private readonly Guid _guid;
    public SerializableGuid(Guid guid)
    {
        _guid = guid;
    }
    public void Serialize(StringWriter sw)
    {
        _guid.Serialize(sw);
    }
}

此适配器包装给定类型的实例,并公开一种序列化它的方法。 请注意,T中的Field<T>现在仅适用于适配器ISerializableField的实例 - 现在Field<T>确定T 的所有实例都可以< / em>被序列化。

就此而言,您不再需要扩展方法,适配器可以自行执行序列化。除非您的代码的其他部分也想要序列化guid。

修改

如果避免为每个可序列化的类型创建一个类是您的首要任务,并且您不介意丢失类型安全性,则可以使用动态调用:

public class Field
{
    private dynamic Value { get; set; }

    public void Serialize(StringWriter sw)
    {
        try
        {
            Value.Serialize(sw);
        }
        catch (RuntimeBinderException ex)
        {
            throw new InvalidOperationException(string.Format("Instance of type {0} does not implement a Serialize method", Value.GetType()), ex);
        }
    }
}

不再需要仿制药了。仿制药强制执行类型安全,我们就把它扔掉了。

答案 2 :(得分:1)

我猜你会为每种类型编写一堆扩展(Serializers)方法。这是可能的,但有一些反思魔法。

public static class Ext
    {
        public static void Serialize(this Guid guid_, StringWriter sw_)
        {
            sw_.Write(guid_.ToString("B"));
        }

        public static void Serialize(this int id, StringWriter sw_)
        {
            sw_.Write(id.ToString());
        }


    }

public class Field<T>
{
    public T value;
    private static Lazy<Action<T, StringWriter>> _serializer = new Lazy<Action<T, StringWriter>>(() => (Action<T, StringWriter>)Delegate.CreateDelegate(typeof(Action<T, StringWriter>), typeof(Ext), "Serialize"), true);


    public void Serialize(StringWriter sw_)
    {
        _serializer.Value(value, sw_);
    }

}

答案 3 :(得分:0)

Field类中的问题是,T是泛型类型,此时它是“未知”,因此扩展方法不会显示,因为扩展方法是GUID的具体类型。 您可能需要检查T是否为guid,如果是,则执行转换为GUID,然后允许您调用扩展方法 - 所以不确定它是否会编译。

关于你在Foo类中的最后一个例子 - 是不是GUID?如果是这样,你导入Ext类存在的项目和命名空间?如果不是 - 那么它应该会出现。