如何创建其构造函数需要委托函数参数的泛型实例?
How to create an instance of generic type whose constructor requires a delegate function parameter?
我需要在其中使用以下泛型 class 和方法 ParseFrom()
:
public sealed class MessageParser<T> : MessageParser where T : IMessage<T>
{
public MessageParser(Func<T> factory); //constructor
public T ParseFrom(byte[] data);
}
现在,我在编译时不知道这个 class 的参数类型,所以我使用类型反射和 MakeGenericType()
方法来做到这一点:
//Assuming itemInstance is given as input parameter
Type typeArgument = itemInstance.GetType();
Type genericClass = typeof(MessageParser<>);
var genericType = genericClass.MakeGenericType(typeArgument);
var instance = Activator.CreateInstance(genericType);
它给我一个运行时错误:MessageParser<> 没有无参数构造函数。但是当我尝试将 Func<T> factory
作为 CreateInstance()
的参数传递时:
var instance = Activator.CreateInstance(genericType, () => Activator.CreateInstance(typeArgument));
它给我一个编译错误:无法将 lambda 表达式转换为类型 'string' 因为它不是委托类型。我在这里使用了错误的委托函数语法吗?
动态构造一个未知类型的委托不像使用反射调用方法那么容易,所以最简单的选择是只写一个静态类型的方法来构造委托,然后使用反射调用它.
public class DelegateCreator
{
public static Func<T> MakeConstructorStatically<T>()
{
return Activator.CreateInstance<T>;
}
public static object MakeConstructorDynamically(Type type)
{
return typeof(DelegateCreator)
.GetMethod(nameof(MakeConstructorStatically))
.MakeGenericMethod(type)
.Invoke(null, Array.Empty<object>());
}
}
要通过反思创建 Func<T>
,CreateDelegate
是必经之路。因此,需要一种具有预期签名的方法——包括类型限制(T 是 IMessage)。
以下是如何让它发挥作用的方法。
缺点是,您仍然需要使用反射来调用解析器的方法,至少是那些使用类型参数的方法:
public class CreateParserLateBound {
//The method with the matching signature
public static T MessageParserFactory<T>()
where T : IMessage<T>
{
//your factory code, you pass to MessageParser(Func<T> factory) goes here...
return default(T);
}
...
// itemInstance == item that is IMesage<T>, with T unknown at compiletime;
var itemType = itemInstance.GetType();
var boundParserType = typeof(MessageParser<>).MakeGenericType(itemType);
var boundFuncType = typeof(Func<>).MakeGenericType(itemType);
var factoryMethodInstance = typeof(CreateParserLateBound )
.GetMethod("MessageParserFactory")
.MakeGenericMethod(itemType)
.CreateDelegate(boundFuncType);
var parserInstance = Activator.CreateInstance(boundParserType,
new object[]{ factoryMethodInstance } );
//Invoke ParseFrom (also through reflection)
byte[] data = {1,2,3,4};
boundParserType.InvokeMember("ParseFrom",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null,
parserInstance, new object[] {data});
完整的可运行代码@https://dotnetfiddle.net/RIOEXA
简单的答案是编写您自己的通用方法,然后通过反射调用它。
public static class Foo
{
public static MessageParser<T> CreateParser<T>() where T : IMessage<T>, new()
=> new MessageParser<T>(() => new T());
private static MethodInfo _createMethod = typeof(Foo)
.GetMethods()
.Where(m => m.Name == nameof(CreateParser) && m.IsGenericMethod)
.Single();
public static MessageParser CreateParser(Type type)
=> (MessageParser)_createMethod.MakeGenericMethod(type)
.Invoke(null, new object[] { });
}
我需要在其中使用以下泛型 class 和方法 ParseFrom()
:
public sealed class MessageParser<T> : MessageParser where T : IMessage<T>
{
public MessageParser(Func<T> factory); //constructor
public T ParseFrom(byte[] data);
}
现在,我在编译时不知道这个 class 的参数类型,所以我使用类型反射和 MakeGenericType()
方法来做到这一点:
//Assuming itemInstance is given as input parameter
Type typeArgument = itemInstance.GetType();
Type genericClass = typeof(MessageParser<>);
var genericType = genericClass.MakeGenericType(typeArgument);
var instance = Activator.CreateInstance(genericType);
它给我一个运行时错误:MessageParser<> 没有无参数构造函数。但是当我尝试将 Func<T> factory
作为 CreateInstance()
的参数传递时:
var instance = Activator.CreateInstance(genericType, () => Activator.CreateInstance(typeArgument));
它给我一个编译错误:无法将 lambda 表达式转换为类型 'string' 因为它不是委托类型。我在这里使用了错误的委托函数语法吗?
动态构造一个未知类型的委托不像使用反射调用方法那么容易,所以最简单的选择是只写一个静态类型的方法来构造委托,然后使用反射调用它.
public class DelegateCreator
{
public static Func<T> MakeConstructorStatically<T>()
{
return Activator.CreateInstance<T>;
}
public static object MakeConstructorDynamically(Type type)
{
return typeof(DelegateCreator)
.GetMethod(nameof(MakeConstructorStatically))
.MakeGenericMethod(type)
.Invoke(null, Array.Empty<object>());
}
}
要通过反思创建 Func<T>
,CreateDelegate
是必经之路。因此,需要一种具有预期签名的方法——包括类型限制(T 是 IMessage
以下是如何让它发挥作用的方法。
缺点是,您仍然需要使用反射来调用解析器的方法,至少是那些使用类型参数的方法:
public class CreateParserLateBound {
//The method with the matching signature
public static T MessageParserFactory<T>()
where T : IMessage<T>
{
//your factory code, you pass to MessageParser(Func<T> factory) goes here...
return default(T);
}
...
// itemInstance == item that is IMesage<T>, with T unknown at compiletime;
var itemType = itemInstance.GetType();
var boundParserType = typeof(MessageParser<>).MakeGenericType(itemType);
var boundFuncType = typeof(Func<>).MakeGenericType(itemType);
var factoryMethodInstance = typeof(CreateParserLateBound )
.GetMethod("MessageParserFactory")
.MakeGenericMethod(itemType)
.CreateDelegate(boundFuncType);
var parserInstance = Activator.CreateInstance(boundParserType,
new object[]{ factoryMethodInstance } );
//Invoke ParseFrom (also through reflection)
byte[] data = {1,2,3,4};
boundParserType.InvokeMember("ParseFrom",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null,
parserInstance, new object[] {data});
完整的可运行代码@https://dotnetfiddle.net/RIOEXA
简单的答案是编写您自己的通用方法,然后通过反射调用它。
public static class Foo
{
public static MessageParser<T> CreateParser<T>() where T : IMessage<T>, new()
=> new MessageParser<T>(() => new T());
private static MethodInfo _createMethod = typeof(Foo)
.GetMethods()
.Where(m => m.Name == nameof(CreateParser) && m.IsGenericMethod)
.Single();
public static MessageParser CreateParser(Type type)
=> (MessageParser)_createMethod.MakeGenericMethod(type)
.Invoke(null, new object[] { });
}