开发中,经常需要用到很多容器功能,而容器级别的功能大都是Reflection(类反射)所提供的,本文就是借这个名来看看如何在.NET 内存中动态构造类并重载某函数,具体的例子代码如下:
原类:TestClass
内存中动态生成类: TestClassEx
内存中动态生成类的工厂类:DynamicFactory
接口: ITest
复制以及重载的目标方法:TestMethod
调用集成采用Console方式进行,我们分别看看各个类的代码;
TestClass.cs
public class TestClass
{
private long m_lUsers = 0;
private int m_iUsers = 0;
private string m_strTest = null;
private object m_oTest = null;
public TestClass()
{
m_lUsers++;
m_iUsers++;
m_strTest = "aa";
m_oTest = new object();
}
public long Users
{
set
{
m_lUsers = value;
}
get
{
return m_lUsers;
}
}
public void TestMethod()
{
System.Console.WriteLine("TestMethod: Message output from Emit Console...");
m_lUsers = 50;
m_lUsers += 100;
System.Console.WriteLine("Users :" + Convert.ToString(m_lUsers) + " people in current context");
}
public void EndMethod()
{
System.Console.WriteLine("EndMethod:Ending message logic*****");
}
}
ITest.cs
public interface ITest
{
void TestMethod();
}
DynamicFactory.cs
public class DynamicFactory
{
private AssemblyBuilder assemblyBuilder = null;
private ModuleBuilder moduleBuilder = null;
private TypeBuilder typeBuilder = null;
private ILGenerator generator = null;
public DynamicFactory()
{
}
public Type GenerateClass(Type typeForMirror)
{
this.GenerateAssembly();
//build a type
this.typeBuilder = moduleBuilder.DefineType("TestClassEx",
TypeAttributes.Public | TypeAttributes.BeforeFieldInit,
typeForMirror, new Type[] { typeof(ITest) });
this.GenerateConstruct(typeBuilder);
this.BuildFields(typeForMirror);
this.BuildMethod(typeForMirror);
Type typeReturn = this.typeBuilder.CreateType(); //Close the type
assemblyBuilder.Save("ExtendingAssembly.dll");
return typeReturn;
}
private void BuildFields(Type typeForMirror)
{
FieldInfo[] fieldInfos = typeForMirror.GetFields(BindingFlags.NonPublic | BindingFlags.Public);
for (int i = 0; i < fieldInfos.Length; i++)
{
FieldInfo fi = fieldInfos[i];
typeBuilder.DefineField(fi.Name, fi.FieldType, fi.Attributes);
}
}
private void GenerateAssembly()
{
AppDomain currentDomain = Thread.GetDomain();
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "Extending";
assemblyBuilder = currentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
this.moduleBuilder = assemblyBuilder.DefineDynamicModule(
"ExtendingAssembly",
"ExtendingAssembly.dll");
}
private void GenerateConstruct(TypeBuilder typeBuilder)
{
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
System.Type.EmptyTypes);
this.generator = constructorBuilder.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Call, typeBuilder.BaseType.GetConstructor(System.Type.EmptyTypes));
generator.Emit(OpCodes.Ret);
}
/// <summary>
/// Attention: here we need to overload the specified TestMethod function for our EmitConsole project.
/// So the MethodAttributes.Virtual is necessary when we rebuild this function in memory.
/// </summary>
/// <param name="typeForMirror"></param>
private void BuildMethod(Type typeForMirror)
{
// Now, let's build a method and add a custom attribute to it.
MethodBuilder methodBuilder = typeBuilder.DefineMethod("TestMethod",
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.Virtual | MethodAttributes.NewSlot,
null,
new Type[] {});
this.generator = methodBuilder.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Call, typeForMirror.GetMethod("TestMethod"));
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Call, typeForMirror.GetMethod("EndMethod"));
generator.Emit(OpCodes.Ret);
}
}
console的集成类program.cs:
class Program
{
static void Main(string[] args)
{
DynamicFactory df = new DynamicFactory();
// type for mirroring testing method
Type typeMirror = df.GenerateClass(typeof(TestClass));
object o = Activator.CreateInstance(typeMirror);
TestClass tc = (TestClass)o;
tc.TestMethod();
ITest it = (ITest)Activator.CreateInstance(typeMirror);
it.TestMethod();
System.Console.WriteLine("Ending finally...");
}
}
从最后的运行效果大家可以分析看看,采用TestClass强转后的instance与不转使用interface来直接集成的program最后的结果不一样,自己可以分析看看是啥原因产生的。
本文中未提及到的Enum, Property等的复制产生可以参照:http://www.codeproject.com/KB/cs/DLR.aspx
能够在内存中动态重组类的作用非常大,可以实现基于AOP的拦截,实现基于标注的各类应用譬如自动组装Transaction、依赖注入等众多的高级应用,对于编写框架应用作用无疑巨大。