问题
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