문제

How can the following be created via reflection?

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

Here's what I have so far:

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

The sample is the way it is as most of the required info will not be known and will be supplied as parameters.

Now, the "instance" assignation will throw an exception. How can I appropriately pass through to the constructor?

Also, how would I cast the result to SomeFramework so I can work with it when I don't know the type parameters?

Thanks!

도움이 되었습니까?

해결책

Assuming SomeFrameworkType<,> definition is something like the following:

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

and the generic type parameters for T1 and T2 are System.String (like in OP's example), then it can be instantiated via reflection using the following codes:

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, we need to know at least the generic type parameters for casting the resulting object to SomeFrameworkType<,> or (as pointed by Jon G) use the non-generic base type of SomeFrameworkType<,> if any.

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

다른 팁

You're passing a MethodInfo as the constructor parameter, when the constructor expects a delegate which is maybe Action or Func or similar.

Can you change SomeFrameworkType to take MethodInfo in its constructor?

Otherwise you could, instead of myMethod in the final line, use something like s => myMethod.Invoke(s)

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

Assuming the delegate signature of the constructor expects a return type of object, this should work.

Re: casting the result, if you want to use the object in a strongly typed manner, you need to cast it to a type that you know at compile time. If SomeFrameworkType<,> has a non-generic base type then you could as- or bracket-cast to that type and use the resulting object.

You can just pass the lambda as a parameter, although you will have to qualify it with some delegate type (System.Action, System.Func or similar)

This code works for me:

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

In fact, we don't need Activator.CreateInstance at all - we can just get the specific constructor we want, and then invoke it. This would be faster if we were creating many instances of the same type of object. Example:

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

What if you want to invoke methods on instance without casting it? Just use InvokeMember, like this

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

If you don't know what kind of callback method you'll need, then it seems like there will be a problem, because you always want to call MyClass.MyMethod, which has a fixed known type. for example: if the constructor to SomeFrameworkType<int, int> requires you to also pass a delegate of type Action<int>, then this doesn't line up with MyClass.MyMethod which may take a string parameter. You'd need to do one of the following: - create some extra wrapper methods which would convert the int to a string (or similar), and then pass those wrapper methods when creating the instance - create a bunch of overloads of MyClass.MyMethod with different parameter types, and have some logic to select the correct one - make MyClass.MyMethod generic, and do something like this:

// 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);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top