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

9
以下内容如何通过反射创建?
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);

样例代码中的大部分必要信息会作为参数提供,因此它看起来是这个样子的。

现在,“instance”分配将抛出异常。我该如何适当地通过构造函数传递参数?

另外,当我不知道类型参数时,我该如何将结果转换为SomeFramework以便进行操作?

谢谢!


你不仅仅是传递一个方法,而是传递一个匿名方法作为委托。你需要构造这个委托。 - Lasse V. Karlsen
抱歉我没有表达清楚。这就是我的问题。我该如何构造它以作为参数传递?我不想调用该方法(这将在稍后发生)。 - Richard
3个回答

4
假设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);

据我所知,我们需要至少知道通用类型参数才能将结果的object强制转换为SomeFrameworkType<,>,或者(如Jon G所指出的)使用SomeFrameworkType<,>的非泛型基类型(如果有)。
var casted = instance as SomeFrameworkType<string, string>;

我不得不修改CreateDelegate,以便使用myClass的构建实例(还要使用Activator.CreateInstance),然后一切都OK了,谢谢! - Richard

0

你正在将MethodInfo作为构造函数参数传递,但构造函数期望一个委托,可能是Action或Func或类似的。

你能否更改SomeFrameworkType以在其构造函数中接受MethodInfo?

否则,你可以在最后一行中使用类似s => myMethod.Invoke(s)的内容来代替myMethod。

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

假设构造函数的委托签名期望返回一个对象类型,那么这应该可以工作。

关于转换结果,如果你想以强类型方式使用对象,你需要将其转换为编译时已知的类型。如果SomeFrameworkType<,>有一个非泛型基类型,那么你可以将其转换为该类型并使用生成的对象。


抱歉,那无法编译。 - Richard

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

我正在尝试按照你的示例操作,但是我无法使其工作,因为像你对“CallTheCallback()”的定义在这里没有声明,所以很难跟进。你能否在此处添加完整的代码,并不假设我知道其余部分的样子。谢谢。 - Gerrie Pretorius
@GerriePretorius 这个例子故意不是一个完整的例子,它旨在展示一组你可能会做的事情,而不是一个完美的可复制的例子。基本上:现在你知道了MakeGenericMethod、GetConstructor和Invoke,你可以自己将它们组合起来形成你自己的特定解决方案。 - Orion Edwards

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接