如何调用Activator.CreateInstance,传递方法作为构造函数参数?

时间:2014-01-06 14:33:30

标签: c# reflection system.reflection

如何通过反射创建以下内容?

var t = new SomeFrameworkType<string, string>(s => MyClass.MyMethod(s));

这是我到目前为止所拥有的:

var typeParamOne = Type.GetType("System.String");
var typeParamTwo = Type.GetType("System.String");
var myClass= Type.GetType("ConsoleApplication.MyClass");
var myMethod = myClass.GetMethod("MyMethod");
var type = typeof(SomeFrameworkType<,>);
var typeArgs = new[] {typeParamOne, typeParamTwo };
var constructed= type.MakeGenericType(typeArgs);
var instance = Activator.CreateInstance(constructed, myMethod);

样本就是它的样子,因为大多数所需信息都是未知的,并且将作为参数提供。

现在,“实例”分配将抛出异常。我怎样才能适当地传递给构造函数?

另外,我如何将结果转换为SomeFramework,以便在我不知道类型参数时可以使用它?

谢谢!

3 个答案:

答案 0 :(得分:3)

假设SomeFrameworkType<,>定义类似于以下内容:

class SomeFrameworkType<T1, T2> { 
  public SomeFrameworkType(Func<T1, T2> func) {}
}

T1T2的泛型类型参数为System.String类似于OP的示例),然后可以使用以下方法通过反射实例化代码:

var typeParamOne = typeof (string);
var typeParamTwo = typeof(string);
var myClass = typeof(ConsoleApplication.MyClass);
var myMethod = myClass.GetMethod("MyMethod");
var type = typeof(SomeFrameworkType<,>);
var typeArgs = new[] { typeParamOne, typeParamTwo };
var constructed = type.MakeGenericType(typeArgs);

// construct the delegate
var ctrArgs = typeof(Func<,>).MakeGenericType(typeArgs);
var dlgate = Delegate.CreateDelegate(ctrArgs, myMethod);

var instance = Activator.CreateInstance(constructed, dlgate);

AFAIK ,我们需要至少知道用于将结果object转换为SomeFrameworkType<,>的通用类型参数或(如Jon G所指)使用非泛型基本类型SomeFrameworkType<,>(如果有)。

var casted = instance as SomeFrameworkType<string, string>;

答案 1 :(得分:0)

当构造函数需要一个可能是Action或Func或类似的委托时,您将传递一个MethodInfo作为构造函数参数。

您可以更改SomeFrameworkType以在其构造函数中使用MethodInfo吗?

否则你可以在最后一行中使用类似s =&gt;的myMethod。 myMethod.Invoke(S)

var instance = Activator.CreateInstance(constructed, s => myMethod.Invoke(s));

假设构造函数的委托签名需要返回类型的对象,这应该可行。

Re:转换结果,如果要以强类型方式使用对象,则需要将其转换为编译时知道的类型。如果SomeFrameworkType&lt;,&gt;有一个非泛型的基类型,然后你可以as或括号转换为该类型并使用结果对象。

答案 2 :(得分:0)

您可以将lambda作为参数传递,但您必须使用某种委托类型(System.ActionSystem.Func或类似资格)对其进行限定

此代码适用于我:

var typeParamOne = typeof(string);
var typeParamTwo = typeof(string);

// we could do new Action<string>(s => MyClass.MyMethod(s)); 
// but why bother when the method signature matches already
var callbackMethod = new Action<string>(MyClass.MyMethod);

var type = typeof(SomeFrameworkType<,>);
var constructed = type.MakeGenericType(typeParamOne, typeParamTwo);
var instance = Activator.CreateInstance(constructed, callbackMethod);

var castedInstance = (SomeFrameworkType<string, string>)instance;
castedInstance.CallTheCallback();

实际上,我们根本不需要Activator.CreateInstance - 我们可以获取我们想要的特定构造函数,然后调用它。如果我们创建相同类型对象的许多实例,这将更快。例如:

var type = typeof(SomeFrameworkType<,>);
var constructed = type.MakeGenericType(typeParamOne, typeParamTwo);
var constructor = constructed.GetConstructor(new[]{ typeof(Action<string>) });
var instance = constructor.Invoke(new object[] { callbackMethod });

如果您想在instance上调用方法而不进行投射,该怎么办?只需使用InvokeMember,就像这样

constructed.InvokeMember(
    "CallTheCallback", 
    BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, 
    null, 
    instance, 
    null);

如果你不知道你需要什么样的回调方法,那么似乎会出现问题,因为你总是想要调用具有固定已知类型的MyClass.MyMethod。例如:如果SomeFrameworkType<int, int>的构造函数要求您也传递类型Action<int>的委托,那么这不会与可能需要字符串参数的MyClass.MyMethod对齐。 您需要执行以下操作之一: - 创建一些额外的包装器方法,将int转换为字符串(或类似的),然后在创建instance时传递这些包装器方法 - 使用不同的参数类型创建一堆MyClass.MyMethod的重载,并有一些逻辑来选择正确的参数类型 - 使MyClass.MyMethod通用,并执行以下操作:

// create the delegate type so we can find the appropriate constructor
var delegateType = typeof(Action<>).MakeGenericType(typeParamOne);

// work out concrete type for calling the generic MyMethod
var myMethodType = typeof(MyClass)
    .GetMethod("MyMethod")
    .MakeGenericMethod(typeParamOne);

// create an instance of the delegate type wrapping MyMethod so we can pass it to the constructor
var delegateInstance = Delegate.CreateDelegate(delegateType, myMethodType);

var type = typeof(SomeFrameworkType<,>);
var constructed = type.MakeGenericType(typeParamOne, typeParamTwo);
var instance = Activator.CreateInstance(constructed, delegateInstance);