Calling an extension method on a member variable of type T of class<T>

半城伤御伤魂 提交于 2019-12-11 08:05:20

问题


Given extension method below:

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

and the class:

public class Field<T>
{
    public T value;

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

I would like to do the following, but can't quite figure it out:

public class Foo
{
    public Field<Guid> uid;

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

Obviously real-life situation is more complicated, this is just a bare-bones example.

EDIT

Compiler errors:

error CS1928: 'T' does not contain a definition for 'Serialize' and the best extension method overload 'Ext.Serialize(System.Guid, StringWriter)' has some invalid arguments

error CS1929: Instance argument: cannot convert from 'T' to 'System.Guid'


回答1:


It is not possible because T is not certainly known at compile time whether it is Guid or not.

But you can do something like this

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

This works, since c# compiler knows value is Guid at compile time.


Following way will work but it will defeat the point of "Generics". Not recommended also. To show how to do I give an example
public void Serialize(StringWriter sw_)
{
    if (typeof (T) == typeof (Guid))
    {
        ((Guid)(object)value).Serialize(sw_);
    }
}



回答2:


There is no way to apply a restriction such that "T will only be of type that have extension methods defined for".

If you really need to use generics for this, you'll have to create an adapter for each type that can be serialized, as such:

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);
    }
}

This adapter wraps an instance of a given type and exposes a way to serialize it. Notice that T in Field<T> now only works with instances of the adapter ISerializableField - Now Field<T> knows for sure that all instances of T can be serialized.

On that note, you wouldn't need the extension method anymore, the adapter could perform the serialization itself. Unless other parts of your code will also want to serialize a guid.

Edit

If avoiding creating a class for each type that can be serialized is your top priority, and you don't mind losing type safety, you can use dynamic invokation:

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);
        }
    }
}

No need for generics anymore. Generics enforce type safety and we're throwing that away.




回答3:


I guess you are going to write a bunch of extension(Serializers) methods for every type. It's possible, but with some reflection magic.

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_);
    }

}



回答4:


the problem in your Field class is, T is a generic type and it, at this point, is "unknown" thus the extension method will not be shown as the extension method is of a concrete type for GUID. you would maybe need to check to see if T is of guid and if so, do the cast to GUID which will then allow you to call the extension method - so not sure that it would even compile.

as for your last example in class Foo - is uid a GUID? if so did you import the project and namespace where your Ext class exists? if not - you should then it will show up.



来源:https://stackoverflow.com/questions/19972751/calling-an-extension-method-on-a-member-variable-of-type-t-of-classt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!