[英]System.Reflection.Emit: bind together a class and an interface
我想將A
類和接口B
綁定在一起並創建一個C
類,以便C
實現接口B
也:
C
有一個構造函數,它將類型A
的對象作為構造函數,我們稱之為i
A
到B
所有屬性的地圖(假設地圖中的所有屬性具有相同的類型),然后它使用來自i
的屬性值。 例如:
class A { public string Name { get; set; } }
interface B { string Name { get; set; } }
class C : B {
private readonly A _i;
public C(A i) {
_i = i;
}
public string Name
{
get => _i.Name;
set => _i.Name = value;
}
}
這就是我所做的(通常我的意思是B
和來源我的意思是A
):
/// <summary>
/// Creates a new type dynamically
/// </summary>
public class CustomTypeGenerator<TSource, TCommon>
{
private readonly TypeBuilder _tb;
private readonly FieldBuilder _entityFieldBldr;
private readonly Type _srcType;
/// <summary>
/// Initialize custom type builder
/// </summary>
public CustomTypeGenerator(IEnumerable<(string CommonPrpName, Type Type, string SourcePrpName)> members)
{
var cmType = typeof(TCommon);
_srcType = typeof(TSource);
if (!cmType.IsInterface)
{
throw new Exception("Type has to be an interface");
}
const string assemblyName = "DynamicAseembly123";
const string typeSignature = "DynamicType123";
var assemblyBuilder =
AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module123");
_tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout);
_tb.AddInterfaceImplementation(cmType);
_entityFieldBldr = EmitSourceField();
_tb.DefineDefaultConstructor(
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.RTSpecialName);
var constructorBuilder = _tb.DefineConstructor(MethodAttributes.Public,
CallingConventions.Standard,
new[] {_srcType});
constructorBuilder.DefineParameter(0, ParameterAttributes.None, "entity");
var constructorIl = constructorBuilder.GetILGenerator();
constructorIl.Emit(OpCodes.Ldarg_0);
constructorIl.Emit(OpCodes.Ldarg_1);
constructorIl.Emit(OpCodes.Stfld, _entityFieldBldr);
constructorIl.Emit(OpCodes.Ret);
foreach (var (commonPrpName, type, sourcePrpName) in members)
{
EmitProperty(commonPrpName, type, sourcePrpName);
}
EmittedType = _tb.CreateType();
}
public Type EmittedType { get; }
private FieldBuilder EmitSourceField()
{
var entityBldr = _tb.DefineField("_" + "entity", _srcType, FieldAttributes.Private);
return entityBldr;
}
private void EmitProperty(string cPn, Type cmPt, string sPn)
{
var srcProp = _srcType.GetProperty(sPn, BindingFlags.Public | BindingFlags.Instance);
var propertyBldr = _tb.DefineProperty(cPn, PropertyAttributes.HasDefault, cmPt, null);
var getterMethodInfo = srcProp.GetMethod ?? throw new Exception("Missing getter!");
var getPropMthdBldr = _tb.DefineMethod($"get_{cPn}",
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
cmPt, Type.EmptyTypes);
var getIl = getPropMthdBldr.GetILGenerator();
var getProperty = getIl.DefineLabel();
var exitGet = getIl.DefineLabel();
getIl.MarkLabel(getProperty);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, _entityFieldBldr);
getIl.Emit(OpCodes.Call, getterMethodInfo);
getIl.Emit(OpCodes.Dup);
getIl.MarkLabel(exitGet);
getIl.Emit(OpCodes.Ret);
var setterMethodInfo = srcProp.SetMethod ?? throw new Exception("Missing setter!");
var setPropMthdBldr = _tb.DefineMethod($"set_{cPn}",
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] {cmPt});
var setIl = setPropMthdBldr.GetILGenerator();
var modifyProperty = setIl.DefineLabel();
var exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, _entityFieldBldr);
setIl.Emit(OpCodes.Ldarg_1);
getIl.Emit(OpCodes.Call, setterMethodInfo);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBldr.SetGetMethod(getPropMthdBldr);
propertyBldr.SetSetMethod(setPropMthdBldr);
}
}
我得到的例外:
程序集'DynamicAseembly123,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null'類型'DynamicType123'中的方法'get_Name'沒有實現。
感謝您提供任何幫助或提示。
更新 :我使用ILSpy
監視我的虛擬示例代碼上生成的IL:
.class private auto ansi beforefieldinit ConsoleApp1.C
extends [System.Runtime]System.Object
implements ConsoleApp1.B
{
// Fields
.field private initonly class ConsoleApp1.A _i
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor (
class ConsoleApp1.A i
) cil managed
{
// Method begins at RVA 0x206a
// Code size 16 (0x10)
.maxstack 8
// (no C# code)
IL_0000: ldarg.0
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
// _i = i;
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfld class ConsoleApp1.A ConsoleApp1.C::_i
// (no C# code)
IL_000f: ret
} // end of method C::.ctor
.method public final hidebysig specialname newslot virtual
instance string get_Name () cil managed
{
// Method begins at RVA 0x207b
// Code size 12 (0xc)
.maxstack 8
// return _i.Name;
IL_0000: ldarg.0
IL_0001: ldfld class ConsoleApp1.A ConsoleApp1.C::_i
IL_0006: callvirt instance string ConsoleApp1.A::get_Name()
// (no C# code)
IL_000b: ret
} // end of method C::get_Name
.method public final hidebysig specialname newslot virtual
instance void set_Name (
string 'value'
) cil managed
{
// Method begins at RVA 0x2088
// Code size 14 (0xe)
.maxstack 8
// _i.Name = value;
IL_0000: ldarg.0
IL_0001: ldfld class ConsoleApp1.A ConsoleApp1.C::_i
IL_0006: ldarg.1
IL_0007: callvirt instance void ConsoleApp1.A::set_Name(string)
// (no C# code)
IL_000c: nop
IL_000d: ret
} // end of method C::set_Name
// Properties
.property instance string Name()
{
.get instance string ConsoleApp1.C::get_Name()
.set instance void ConsoleApp1.C::set_Name(string)
}
} // end of class ConsoleApp1.C
我使用ILSpy
的提示來獲得一些提示,這是我更新的C#代碼:
/// <summary>
/// Creates a new type dynamically
/// </summary>
public class CustomTypeGenerator<TSource, TCommon>
{
private readonly TypeBuilder _tb;
private readonly FieldBuilder _entityFieldBldr;
private readonly Type _srcType;
/// <summary>
/// Initialize custom type builder
/// </summary>
public CustomTypeGenerator(Dictionary<string, (Type Type, string SourcePrpName)> members)
{
var objType = typeof(object);
var cmType = typeof(TCommon);
_srcType = typeof(TSource);
if (!cmType.IsInterface)
{
throw new Exception("Type has to be an interface");
}
const string assemblyName = "DynamicAssembly123";
const string typeSignature = "DynamicType123";
var assemblyBuilder =
AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module123");
_tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Serializable |
TypeAttributes.Class |
TypeAttributes.Sealed |
TypeAttributes.AutoLayout, objType);
_tb.AddInterfaceImplementation(cmType);
_entityFieldBldr = EmitSourceField();
_tb.DefineDefaultConstructor(
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.RTSpecialName);
var constructorBuilder = _tb.DefineConstructor(MethodAttributes.Public,
CallingConventions.Standard,
new[] {_srcType});
constructorBuilder.DefineParameter(0, ParameterAttributes.None, "entity");
var constructorIl = constructorBuilder.GetILGenerator();
constructorIl.Emit(OpCodes.Ldarg_0);
constructorIl.Emit(OpCodes.Ldarg_1);
constructorIl.Emit(OpCodes.Stfld, _entityFieldBldr);
constructorIl.Emit(OpCodes.Ret);
foreach (var (commonPrpName, (type, sourcePrpName)) in members)
{
EmitProperty(commonPrpName, type, sourcePrpName);
}
EmittedType = _tb.CreateType();
}
public Type EmittedType { get; }
private FieldBuilder EmitSourceField()
{
var entityBldr = _tb.DefineField("_" + "entity", _srcType,
FieldAttributes.Private |
FieldAttributes.InitOnly);
return entityBldr;
}
private void EmitProperty(string cPn, Type cmPt, string sPn)
{
var srcProp = _srcType.GetProperty(sPn, BindingFlags.Public | BindingFlags.Instance);
var getterMethodInfo = srcProp.GetMethod ?? throw new Exception("Missing getter!");
var getPropMthdBldr = _tb.DefineMethod($"get_{cPn}",
MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
cmPt, Type.EmptyTypes);
var getIl = getPropMthdBldr.GetILGenerator();
var getPropertyLbl = getIl.DefineLabel();
var exitGetLbl = getIl.DefineLabel();
getIl.MarkLabel(getPropertyLbl);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, _entityFieldBldr);
getIl.Emit(OpCodes.Callvirt, getterMethodInfo);
getIl.MarkLabel(exitGetLbl);
getIl.Emit(OpCodes.Ret);
var setterMethodInfo = srcProp.SetMethod ?? throw new Exception("Missing setter!");
var setPropMthdBldr = _tb.DefineMethod($"set_{cPn}",
MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] {cmPt});
var setIl = setPropMthdBldr.GetILGenerator();
var modifyPropertyLbl = setIl.DefineLabel();
var exitSetLbl = setIl.DefineLabel();
setIl.MarkLabel(modifyPropertyLbl);
setIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, _entityFieldBldr);
setIl.Emit(OpCodes.Ldarg_1);
getIl.Emit(OpCodes.Callvirt, setterMethodInfo);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSetLbl);
setIl.Emit(OpCodes.Ret);
var propertyBldr = _tb.DefineProperty(cPn, PropertyAttributes.None, cmPt, null);
propertyBldr.SetGetMethod(getPropMthdBldr);
propertyBldr.SetSetMethod(setPropMthdBldr);
}
}
原始問題是因為getter / setter方法需要MethodAttributes.Virtual
才能能夠隱式實現相應的接口方法。
添加該標志足以實現所需的隱式接口實現。 對於顯式接口實現,你必須使用DefineMethodOverride如在另一篇文章中提到,但MethodAttributes.Virtual
仍然是必需的。 實際上你也可以使用DefineMethodOverride
和隱式實現 - 它不會受到傷害,但是不需要。
以上內容已在您更新的代碼中修復。 但現在它生成了InvalidProgramException
。 這是由於(復制/粘貼我猜)在set方法體生成中使用get IL生成器變量引起的:
setIl.MarkLabel(modifyPropertyLbl);
setIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, _entityFieldBldr); // <--
setIl.Emit(OpCodes.Ldarg_1);
getIl.Emit(OpCodes.Callvirt, setterMethodInfo); // <--
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSetLbl);
setIl.Emit(OpCodes.Ret);
這當然會為getter和setter發出無效代碼。 使用正確的變量,一切都會好的。 基本上
var setIl = setPropMthdBldr.GetILGenerator();
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldfld, _entityFieldBldr);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Callvirt, setterMethodInfo);
setIl.Emit(OpCodes.Ret);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.