2016-11-07 66 views
1

我试图Reflection.Emit泛型类实现了使用该类的泛型参数构造的泛型接口,例如,这样的:Reflection.Emit:获取具有GenericTypeParameterBuilder类型参数的构造类型的MethodInfo

class Foo<U>: IEquatable<U> 
{ 
    bool IEquatable<U>.Equals(U other) { /* ... */ } 
} 

我挣扎得到MethodInfoIEquatable<U>.Equals(U)方法。

反射构造类型IEquatable<U>抛出一个NotSupportedException因为UGenericTypeParameterBuilder,而TypeBuilder.GetMethod(IEquatable<U>, IEquatable<T>.Equals(T))返回一个怪异IEquatable<U>.Equals(T)方法。

任何帮助表示赞赏!

测试代码:

// define class Foo<U> 
var tb = moduleBuilder.DefineType("Foo"); 
var genParams = tb.DefineGenericParameters("U"); 

// IEquatable<T>.Equals(T) method 
var miEqualsT = typeof(IEquatable<>).GetMethod("Equals", typeof(IEquatable<>).GetGenericArguments()); 

// IEquatable<U> constructed type 
var iEquatableU = typeof(IEquatable<>).MakeGenericType(genParams); 

// now trying to get IEquatable<U>.Equals(U) method 
MethodInfo miEqualsU; 
try { miEqualsU = iEquatableU.GetMethod("Equals", genParams); } 
catch (NotSupportedException) { Console.WriteLine("Reflecting constructed interface not supported."); } 

miEqualsU = TypeBuilder.GetMethod(iEquatableU, miEqualsT); 
var declaringType =$"{miEqualsU.DeclaringType.Name}<{miEqualsU.DeclaringType.GenericTypeArguments[0].Name}>"; 
var parameterType = miEqualsU.GetParameters()[0].ParameterType; 
Console.WriteLine($"TypeBuilder.GetMethod() returns {declaringType}.{miEqualsU.Name}({parameterType.Name})"); 

// OUTPUT: 
// Reflecting constructed interface not supported. 
// TypeBuilder.GetMethod() returns IEquatable`1<U>.Equals(T) 

回答

3

你有正确的方法与miEqualsT了。这里是你如何实现该方法的接口:

var assemblyName = new AssemblyName { Name = "asd" }; 
var moduleBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(assemblyName.Name); 

var tb = moduleBuilder.DefineType("Foo"); 
var genParams = tb.DefineGenericParameters("U"); 
var miEqualsT = typeof(IEquatable<>).GetMethod("Equals", typeof(IEquatable<>).GetGenericArguments()); 

var myMethod = tb.DefineMethod("Equals", MethodAttributes.Public | MethodAttributes.Virtual, typeof(bool), genParams); 

var il = myMethod.GetILGenerator(); 
il.Emit(OpCodes.Ldstr, "I was invoked!"); 
il.Emit(OpCodes.Call, GetMethod<string>(a => Console.WriteLine(a))); 
il.Emit(OpCodes.Ldc_I4_1); 
il.Emit(OpCodes.Ret); 

tb.AddInterfaceImplementation(typeof(IEquatable<>).MakeGener‌​icType(genParams)); 
tb.DefineMethodOverride(myMethod, miEqualsT); 

var t = tb.CreateType(); 

//Hack for demonstration 
private MethodInfo GetMethod<T>(Expression<Action<T>> thing) 
{ 
    return ((thing as LambdaExpression).Body as MethodCallExpression).Method; 
} 

,然后用它:

var genericType = t.MakeGenericType(typeof(int)); 
var tObj = Activator.CreateInstance(genericType); 

var method = genericType.GetMethods() 
    .Where(m => m.Name == "Equals" && m.DeclaringType == genericType) 
    .First(); 

method.Invoke(tObj, new object[] { 5 }); 

或者:

var tObj = Activator.CreateInstance(genericType); 
DoIt(tObj as IEquatable<int>); 

private void DoIt(IEquatable<int> obj) 
{ 
    obj.Equals(5); 
} 
+0

是的,我认为OP是在解决问题,并试图让他所有的鸭子连续,并认为他需要'MethodInfo'用于'IEquatable .Equals(U o)' - 其中方法信息被认证。在实践中,这样的方法信息对象是不必要的,因为你所需要做的就是用正确的_signature_定义一个方法(就像你所做的那样),然后你就可以很好地去做。 –

+0

谢谢你的回答!我注意到它的确如此,但不相信它是正确的。原来我们发出的东西等同于'Foo :IEquatable {bool IEquatable .Equals(U other){/*...*/}}',这似乎很奇怪。但是我发现它('TypeBuilder.CreateType()'?)可以变得有意义,因为我们永远不可能拥有'class Foo :IEquatable ,IEquatable '。仍然感到惊讶的是,我们必须“实现”一个泛型类型定义。 – tinudu

+0

@tinudu再看一次,我很好奇我写的原始代码甚至是如何工作的。在我看来代码应该是:'tb.AddInterfaceImplementation(typeof(IEquatable <>)。MakeGenericType(genParams))''。经过一些测试(使'Foo '和添加实现'IEquatable <>')产生'Foo :IEquatable ',所以我不太确定在分配泛型参数方面幕后发生了什么。我们需要明确地添加* any *方法的实现(不仅仅是泛型) - 我们只是被C#自动提供了一个基于匹配签名的实现 – Rob