c# - reflection in the System.Reflection.Emit namespace - AssemblyBuilder
15621 단어 C#
In this code, we are going to show how to create some assemblies which you can both run and save to a file and load execute again.
the referenced material is AssemblyBuilder class . First and foremost, let's see the code.
class DemoAssemblyBuilder
{
internal static void Main(string[] args)
{
// An assembly consists of one or more modules, each of which
// contains zero or more types. This code creates a single-module
// assembly, the most common case. The module contains one type,
// named "MyDynamicType", that has a private field, a property
// that gets and sets the private field, constructors that
// initialize the private field, and a method that multiplies
// a user-supplied number by the private field value and returns
// the result. In C# the type might look like this:
/*
public class MyDynamicType
{
private int m_number;
public MyDynamicType() : this(42) {}
public MyDynamicType(int initNumber)
{
m_number = initNumber;
}
public int Number
{
get { return m_number; }
set { m_number = value; }
}
public int MyMethod(int multiplier)
{
return m_number * multiplier;
}
}
*/
AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(
aName,
AssemblyBuilderAccess.RunAndSave); // the generated asssembly will both be used to run and saved as some Assembly file for later use
// other values include AssemblyBuilderAccess.Run, AssemblyBuilderAccess.Save, AssemblyBuilderAccess.ReflectionOnly, AssemblyBuilderAccess.RunAndCollect
// for a single-module assembly, the module name is usually
// the assembly name plus an extension
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll"); // since we are going to save, we have to give it a name
TypeBuilder tb = mb.DefineType(
"MyDynamicType",
TypeAttributes.Public);
// Add a private field of type int (Int32)
FieldBuilder fbNumber = tb.DefineField(
"m_number",
typeof(int),
FieldAttributes.Private); // private int m_number
// Define a constructor that takes an integer argument and
// stores it in the private field.
Type[] parameterTypes = { typeof(int) };
ConstructorBuilder ctor1 = tb.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
parameterTypes);
ILGenerator ctor1IL = ctor1.GetILGenerator();
// For a constructor, argument zero is a reference to the new
// instance. Push it on the stack before calling the base
// class constructor. Specify the default constructor of the
// base class (System.Object) by passing an empty array of
// types (Type.EmptyTypes) to GetConstructor.
ctor1IL.Emit(OpCodes.Ldarg_0); // for a class method, the first OpCodes.Ldarg_0 is always necessary because it is the 'this' pointer
ctor1IL.Emit(OpCodes.Ldarg_1);
ctor1IL.Emit(OpCodes.Stfld, fbNumber);
ctor1IL.Emit(OpCodes.Ret);
// Define a default constructor that supplies a default value
// for the private field. For parameter types, pass the empty
// array of types or pass null.
ConstructorBuilder ctor0 = tb.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
Type.EmptyTypes);
ILGenerator ctor0IL = ctor0.GetILGenerator();
// For a constructor, argument zero is a reference to the new
// instance. Push it on the stack before pushing the default
// value on the stack, then call constructor ctor1.
ctor0IL.Emit(OpCodes.Ldarg_0);
ctor0IL.Emit(OpCodes.Ldc_I4_S, 42); // Ldc stands for load constant as Int 4 bytes (short form) onto the stack
ctor0IL.Emit(OpCodes.Call, ctor1); // this: this(42)
ctor0IL.Emit(OpCodes.Ret); // return
// Define a property named Number that gets and sets the private
// field.
//
// The last argument of DefineProperty is null, because the
// property has no parameters. (If you don't specify null, you must
// specify an array of Type objects. For a parameterless property,
// use the built-in array with no elements: Type.EmptyTypes)
PropertyBuilder pbNumber = tb.DefineProperty(
"Number",
PropertyAttributes.HasDefault, // has default, what is the default value?
typeof(int), // type is int
null
);
// The property "set" and property "get" methods require a special
// set of attributes.
MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; // we have the following attribute because we want to assign setter/getter to the code
// Define the "get" accessor method for Number. The method returns
// an integer and has no arguments. (Note that null could be
// used instead of Types.EmptyTypes)
MethodBuilder mbNumberGetAccessor = tb.DefineMethod(
"get_Number", // MethodAttributes.SpecialName
getSetAttr,
typeof(int),
Type.EmptyTypes); // get_Number(void)
ILGenerator numberGetIL = mbNumberGetAccessor.GetILGenerator();
// For an instance property, argument zero is the instance. Load the
// instance, then load the private field and return, leaving the
// field value on the stack.
numberGetIL.Emit(OpCodes.Ldarg_0);
numberGetIL.Emit(OpCodes.Ldfld, fbNumber); // register <- addressof fbNumber;
numberGetIL.Emit(OpCodes.Ret);
// Define the "set" accessor method for Number, which has no return
// type and takes one argument of type int (Int32).
MethodBuilder mbNumberSetAccessor = tb.DefineMethod(
"set_Number",
getSetAttr,
null, // void set_Number(int value)
new Type[] { typeof(int) });
ILGenerator numberSetIL = mbNumberSetAccessor.GetILGenerator();
// Load the instance and then the numeric argument, then store the
// argument in the field.
numberSetIL.Emit(OpCodes.Ldarg_0);
numberSetIL.Emit(OpCodes.Ldarg_1);
numberSetIL.Emit(OpCodes.Stfld, fbNumber);
numberSetIL.Emit(OpCodes.Ret);
// Last, map the "get" and "set" accessor methods to the
// PropertyBuilder. The property is now complete.
pbNumber.SetGetMethod(mbNumberGetAccessor);
pbNumber.SetSetMethod(mbNumberSetAccessor);
// Define a method that accepts an integer argument and returns
// the product of that integer and the private field m_number. This
// time, the array of parameter types is created on the fly.
MethodBuilder meth = tb.DefineMethod(
"MyMethod",
MethodAttributes.Public,
typeof(int),
new Type[] { typeof(int) });
ILGenerator methIL = meth.GetILGenerator();
// To retrieve the private instance field, load the instance it
// belongs to (argument zero). After loading the field, load the
// argument one and then multiply. Return from the method with
// the return value (the product of the two numbers) on the
// execution stack.
methIL.Emit(OpCodes.Ldarg_0); // it is the caller that push the argument to the stack, so in the function body , we assume that we already have the parameters on stack
methIL.Emit(OpCodes.Ldfld, fbNumber); // the OpCodes.Ldflda is to load the address of the field
methIL.Emit(OpCodes.Ldarg_1);
methIL.Emit(OpCodes.Mul);
methIL.Emit(OpCodes.Ret);
// Finish the type.
Type t = tb.CreateType();
// The following line saves the single-module assembly. This
// requires AssemblyBuilderAccess to include Save. You can now
// type "ildasm MyDynamicAsm.dll" at the command prompt, and
// examine the assembly. You can also write a program that has
// a reference to the assembly, and use the MyDynamicType type.
//
ab.Save(aName.Name + ".dll");
// Because AssemblyBuilderAccess includes Run, the code can be
// executed immediately. Start by getting reflection objects for
// the method and the property.
MethodInfo mi = t.GetMethod("MyMethod");
PropertyInfo pi = t.GetProperty("Number");
// Create an instance of MyDynamicType using the default
// display it again. Use null to indicate the property
// has no index
object o1 = Activator.CreateInstance(t);
// Display the value of the property, then change it to 127 and
// display it again. Use null to indicate that the property
Console.WriteLine("o1.Number: {0}", pi.GetValue(o1, null));
pi.SetValue(o1, 127, null);
Console.WriteLine("o1.Number: {0}", pi.GetValue(o1, null));
// has no index.
// Call MyMethod, passing 22, and display the return value, 22
// times 127. Arguments must be passed as an array, even when
// there is only one.
object[] arguments = { 22 };
Console.WriteLine("o1.MyMethod(22): {0} ", mi.Invoke(o1, arguments));
// Create an instance of MyDynamicType using the constructor
// that specifies m_Number. The constructor is identified by
// matching the types in the argument array. In this case,
// the argument array is created on the fly. Display the
// property value.
object o2 = Activator.CreateInstance(t, new object[] { 5280 });
Console.WriteLine("o2.Number : {0}", pi.GetValue(o2, null));
}
}
so, some summary that we can get from the code above:
If you take a look at the generated code then you might be able to find this.
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.1
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly DynamicAssemblyExample
{
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module DynamicAssemblyExample
// MVID: {2F8EC134-454A-4A25-BDB6-53649D2F2973}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x00410000
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi MyDynamicType
extends [mscorlib]System.Object
{
.field private int32 m_number
.method public specialname rtspecialname
instance void .ctor(int32 A_1) cil managed
{
// 8 (0x8)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 MyDynamicType::m_number
IL_0007: ret
} // end of method MyDynamicType::.ctor
.method public specialname rtspecialname
instance void .ctor() cil managed
{
// 12 (0xc)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.s 42
IL_0003: nop
IL_0004: nop
IL_0005: nop
IL_0006: call instance void MyDynamicType::.ctor(int32)
IL_000b: ret
} // end of method MyDynamicType::.ctor
.method public hidebysig specialname instance int32
get_Number() cil managed
{
// 7 (0x7)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ldfld int32 MyDynamicType::m_number
IL_0006: ret
} // end of method MyDynamicType::get_Number
.method public hidebysig specialname instance void
set_Number(int32 A_1) cil managed
{
// 8 (0x8)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 MyDynamicType::m_number
IL_0007: ret
} // end of method MyDynamicType::set_Number
.method public instance int32 MyMethod(int32 A_1) cil managed
{
// 9 (0x9)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldfld int32 MyDynamicType::m_number
IL_0006: ldarg.1
IL_0007: mul
IL_0008: ret
} // end of method MyDynamicType::MyMethod
.property int32 Number()
{
.get instance int32 MyDynamicType::get_Number()
.set instance void MyDynamicType::set_Number(int32)
} // end of property MyDynamicType::Number
} // end of class MyDynamicType
// =============================================================
// *********** ***********************
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
WebView2를 Visual Studio 2017 Express에서 사용할 수 있을 때까지Evergreen .Net Framework SDK 4.8 VisualStudio2017에서 NuGet을 사용하기 때문에 패키지 관리 방법을 packages.config 대신 PackageReference를 사용해야...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.