C# 反射.Emit实现接口的通用方法约束

C# 反射.Emit实现接口的通用方法约束,c#,generics,interface,reflection.emit,type-constraints,C#,Generics,Interface,Reflection.emit,Type Constraints,我正在尝试使用System.Reflection.Emit动态生成接口的实现。为了能够生成泛型方法的实现,我必须正确地将接口方法的所有泛型参数约束应用于实现它的生成类中的方法,但我无法找出基类约束的错误之处 尝试生成类型时,我收到以下错误(已翻译): System.TypeLoadException:“TestAsm,Version=0.0.0,Culture=neutral,PublicKeyToken=null”程序集的“TestImplementation”类型中的方法“Error”试图隐

我正在尝试使用
System.Reflection.Emit
动态生成接口的实现。为了能够生成泛型方法的实现,我必须正确地将接口方法的所有泛型参数约束应用于实现它的生成类中的方法,但我无法找出基类约束的错误之处

尝试生成类型时,我收到以下错误(已翻译):

System.TypeLoadException:“TestAsm,Version=0.0.0,Culture=neutral,PublicKeyToken=null”程序集的“TestImplementation”类型中的方法“Error”试图隐式实现对类型参数约束较弱的接口方法。”

下面是一个简单的示例界面:

public interface ITest
{
    //Base type constraint seems to cause issues, also reproduces with other classes
    void Error<T>() where T : Encoding;

    // these all work as expected when code is generated for them
    //Task<T> A<T>(T input) where T : struct;
    //Task<T> B<T>(T input) where T : class, new();
    //Task<T> C<T>(T input) where T : IComparable<string>, IComparable, ICloneable;
}
公共接口测试
{
//基类型约束似乎会引起问题,也会与其他类一起复制
void Error(),其中T:Encoding;
//当为它们生成代码时,它们都能正常工作
//任务A(T输入),其中T:struct;
//任务B(T输入),其中T:class,new();
//任务C(T输入),其中T:IComparable,IComparable,iClonable;
}
这是类型生成器:

internal class Program
{
    private static Type Build()
    {
        // quite a lot of boilerplate is necessary for this, sorry!
        var asm = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAsm"), AssemblyBuilderAccess.Run);
        var module = asm.DefineDynamicModule(asm.GetName().Name);


        var type = module.DefineType(
            "TestImplementation",
            TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.AutoLayout | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit,
            typeof(object),
            new[] { typeof(ITest) }
        );
        var method = typeof(ITest).GetMethod("Error");

        var m = type.DefineMethod(
            method.Name,
            MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot,
            CallingConventions.Standard | CallingConventions.HasThis,
            typeof(void),
            new Type[0]
        );

        //this is where the constraints are applied, I assume something is missing here
        var constraint = method.GetGenericArguments()[0];
        var constraintBuilder = m.DefineGenericParameters("T")[0];

        constraintBuilder.SetBaseTypeConstraint(constraint.BaseType);
        constraintBuilder.SetInterfaceConstraints(constraint.GetInterfaces());
        constraintBuilder.SetGenericParameterAttributes(constraint.GenericParameterAttributes);
        foreach (var attribute in BuildCustomAttributes(constraint.GetCustomAttributesData()))
        {
            constraintBuilder.SetCustomAttribute(attribute);
        }

        // dummy method body
        var il = m.GetILGenerator();
        il.EmitWriteLine("Sucess!");
        il.Emit(OpCodes.Ret);

        //fails right here \/
        return type.CreateType();
    }

    // I don't think attributes are actually necessary, but just in case..
    private static IEnumerable<CustomAttributeBuilder> BuildCustomAttributes(IEnumerable<CustomAttributeData> customAttributes)
    {
        return customAttributes.Select(attribute =>
        {
            var attributeArgs = attribute.ConstructorArguments.Select(a => a.Value).ToArray();
            var namedPropertyInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType<PropertyInfo>().ToArray();
            var namedPropertyValues = attribute.NamedArguments.Where(a => a.MemberInfo is PropertyInfo).Select(a => a.TypedValue.Value).ToArray();
            var namedFieldInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType<FieldInfo>().ToArray();
            var namedFieldValues = attribute.NamedArguments.Where(a => a.MemberInfo is FieldInfo).Select(a => a.TypedValue.Value).ToArray();
            return new CustomAttributeBuilder(attribute.Constructor, attributeArgs, namedPropertyInfos, namedPropertyValues, namedFieldInfos, namedFieldValues);
        });
    }

    private static void Main(string[] args)
    {
        var t = Build();
        var instance = (ITest)Activator.CreateInstance(t);
        instance.Error<List<object>>();
    }
}
内部类程序
{
私有静态类型Build()
{
//这需要很多样板文件,对不起!
var asm=AssemblyBuilder.DefinedDynamicAssembly(新的AssemblyName(“TestAsm”),AssemblyBuilderAccess.Run);
var module=asm.definedDynamicModule(asm.GetName().Name);
变量类型=module.DefineType(
“测试实施”,
TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.AutoLayout | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit,
类型(对象),
新[]{typeof(ITest)}
);
var方法=类型(ITest).GetMethod(“错误”);
var m=类型定义方法(
方法名称,
MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.hidebysing | MethodAttributes.Virtual | MethodAttributes.NewSlot,
CallingConventions.Standard | CallingConventions.HasThis,
类型(无效),
新类型[0]
);
//这就是应用约束的地方,我假设这里缺少一些东西
var constraint=method.GetGenericArguments()[0];
var constraintBuilder=m.DefineGenericParameters(“T”)[0];
constraintBuilder.SetBaseTypeConstraint(constraint.BaseType);
constraintBuilder.SetInterfaceConstraints(constraint.GetInterfaces());
constraintBuilder.SetGenericParameterAttributes(constraint.GenericParameterAttributes);
foreach(BuildCustomAttributes(constraint.GetCustomAttributesData())中的var属性)
{
constraintBuilder.SetCustomAttribute(属性);
}
//虚拟方法体
var il=m.GetILGenerator();
成功;
发射(操作码Ret);
//就在这里失败了\/
返回类型。CreateType();
}
//我不认为属性实际上是必要的,但以防万一。。
私有静态IEnumerable BuildCustomAttributes(IEnumerable customAttributes)
{
返回customAttributes。选择(属性=>
{
var attributeArgs=attribute.ConstructorArguments.Select(a=>a.Value.ToArray();
var namedPropertyInfos=attribute.NamedArguments.Select(a=>a.MemberInfo).OfType().ToArray();
var namedPropertyValues=attribute.NamedArguments.Where(a=>a.MemberInfo是PropertyInfo)。选择(a=>a.TypedValue.Value)。ToArray();
var namedFieldInfos=attribute.NamedArguments.Select(a=>a.MemberInfo).OfType().ToArray();
var namedFieldValues=attribute.NamedArguments.Where(a=>a.MemberInfo是FieldInfo)。选择(a=>a.TypedValue.Value)。ToArray();
返回新的CustomAttributeBuilder(attribute.Constructor、attributeArgs、namedPropertyInfos、namedPropertyValues、namedFieldInfos、namedFieldValues);
});
}
私有静态void Main(字符串[]args)
{
var t=Build();
var instance=(ITest)Activator.CreateInstance(t);
instance.Error();
}
}
我所尝试的:

  • 添加了我以前认为不必要的CustomAttributes生成
  • 阅读有关发出泛型类型和方法的MSDN文章,但这并不能帮助我理解为什么约束不等价
  • 尝试了其他可能的约束后,只有基类约束似乎失败了
  • 对于
    SetBaseTypeConstraint
  • 使用
    TypeBuilder.CreateMethodOverride
    使用显式实现,这只是将错误消息从“隐式”更改为“显式”
  • 将示例项目创建为.net framework应用程序,而不是.net标准类库

我的约束生成中是否缺少某些内容?我期望调用
constraintBuilder.SetBaseTypeConstraint(constraint.BaseType)
为基类设置约束。

设置任何接口约束都会覆盖BaseType约束或导致错误消息。这解决了问题:

if(constraint.BaseType != null)
{
    constraintBuilder.SetBaseTypeConstraint(constraint.BaseType);
}
else
{
    constraintBuilder.SetInterfaceConstraints(constraint.GetInterfaces());
}
这似乎真的违反直觉,因为我现在应用较少的约束来修复一个错误,即我应用的约束太少。这也适用于以下声明:

void Test<T>() where T: Example, IComparable<Example>`
被替换为

var interfaceList = new List<Type>();
foreach (var restriction in constraint.GetGenericParameterConstraints())
{
        if (restriction.IsClass)
        {
                constraintBuilder.SetBaseTypeConstraint(restriction);
        }
        else
        {
                interfaceList.Add(restriction);
        }
}

if (interfaceList.Count > 0)
{
        constraintBuilder.SetInterfaceConstraints(interfaceList.ToArray());
}
var interfaceList=newlist();
foreach(constraint.GetGenericParameterConstraints()中的变量限制)
{
if(restriction.IsClass)
{
constraintBuilder.SetBaseTypeConstraint(限制);
}
其他的
{
接口列表添加(限制);
}
}
如果(interfaceList.Count>0)
{
constraintBuilder.SetInterfaceConstraints(interfaceList.ToArray());
}
var interfaceList = new List<Type>();
foreach (var restriction in constraint.GetGenericParameterConstraints())
{
        if (restriction.IsClass)
        {
                constraintBuilder.SetBaseTypeConstraint(restriction);
        }
        else
        {
                interfaceList.Add(restriction);
        }
}

if (interfaceList.Count > 0)
{
        constraintBuilder.SetInterfaceConstraints(interfaceList.ToArray());
}