C#笔记_1-动态生成类,程序集
来源:互联网 发布:java的applet过时了吗 编辑:程序博客网 时间:2024/06/08 13:14
利用反射动态创建对象(如string,int,float,bool,以及自定义类等):
“反射”其实就是利用程序集的元数据信息。 反射可以有很多方法,编写程序时请先导入 System.Reflection 命名空间,假设你要反射一个 DLL 中的类,并且没有引用它(即未知的类型): Assembly assembly = Assembly.LoadFile("程序集路径,不能是相对路径"); // 加载程序集(EXE 或 DLL) object obj = assembly.CreateInstance("类的完全限定名(即包括命名空间)"); // 创建类的实例 若要反射当前项目中的类可以为: Assembly assembly = Assembly.GetExecutingAssembly(); // 获取当前程序集 object obj = assembly.CreateInstance("类的完全限定名(即包括命名空间)"); // 创建类的实例,返回为 object 类型,需要强制类型转换 也可以为: Type type = Type.GetType("类的完全限定名"); object obj = type.Assembly.CreateInstance(type); 反射创建类的实例
上述描述中提到的三种方法其实都是大同小异的,核心就是通过System.Reflection.Assembly 类型的CreateInstance方法创建实例。
关于System.Reflection.Assembly 类可以直接在MSDN上查询详细信息http://msdn.microsoft.com/zh-cn/library/system.reflection.assembly(v=vs.110).aspx
那么简单的解释一下这种方法的原理:
1.找到要实例化的类所在的程序集,并将之实例为System.Reflection.Assembly 类的对象
2.利用System.Reflection.Assembly 类提供的CreateInstance方法,创建类的对象
第一次测试,创建一个简单的自定义类型对象<pre>class Test{ private string _strId; public string ID { get { return _strId; } set { _strId = value; } } public Test() { }}
调试结果:显示obj对象的确不为空,证明这种方法可行。
第二次测试,加深难度,测试类的构造函数需要传递参数
首先修改Test类,将其构造函数改为:
public Test(string str){ _strId = str;}
调试结果:直接抛出异常:未找到类型“ReflectionTest.Test”上的构造函数。这是因为CreateInstance方法默认情况下是通过找无参数的构造函数去创建对象的,现在找不到当然会出错,实时上CreateInstance方法提供了3中签名,其中有CreateInstance(String, Boolean, BindingFlags, Binder, Object [], CultureInfo, Object []) 就可以满足这种情况:
修改主函数如下:
Assembly assembly = Assembly.GetExecutingAssembly(); // 获取当前程序集 //object obj = assembly.CreateInstance("ReflectionTest.Test"); //类的完全限定名(即包括命名空间)object[] parameters = new object[1];parameters[0] = "test string";object obj = assembly.CreateInstance("ReflectionTest.Test",true,System.Reflection.BindingFlags.Default,null,parameters,null,null);// 创建类的实例
调试结果:正常,并且对象中变量值也是正确的,但是这离笔者的需求还差很远。继续
第三次测试,继续加深难度,创建string的对象
首先知道string是System.String的别名,所以要创建的是System.String的对象,而System.String在mscorlib.dll中,所以需要将mscorlib.dll实例为System.Reflection.Assembly的对象,这里利用System.Type类型的属性Assembly来实现功能。
System.String的构造函数有很多种,本文中笔者就不墨迹了,采用String( Char []) 。
最终将主函数中代码改为:
Type type = Type.GetType("System.String");object[] parameters = new object[1];char[] lpChar = { 't','e','s','t' };parameters[0] = lpChar;object obj = type.Assembly.CreateInstance("ReflectionTest.Test",true,System.Reflection.BindingFlags.Default,null,parameters,null,null);// 创建类的实例
调试结果:对象为空,失败了,事实上这种方法还有个问题,如将Test类构造函数修改为
public Test(string str){ ID = str;//属性赋值}
调试结果:对象创建成功,但是变量为空
以上问题详细原因笔者现在也无法解释,正在查找相关资料。
解决方案
采用System.Activator 类的CreateInstance方法。
最后见代码:
Type type = Type.GetType("System.String");object[] parameters = new object[1];char[] lpCh = { 't', 'e', 's', 't' };parameters[0] = lpCh;object obj = Activator.CreateInstance(type, parameters);
调试结果:对象创建成功,且变量值正常
结论
采用System.Activator 类的CreateInstance方法,要比System.Reflection.Assembly的CreateInstance简单有效很多。
更深一步:
首先需要知道动态创建这些类型是使用的一些什么技术呢?其实只要相关动态加载程序集呀,类呀,都是使用反射,那么动态创建也一样使用的是反射,是属于反射的技术!也就是将对象或者数据映射成一个对象或者程序集保存起来而已。
首先我们需要了解每个动态类型在.net中都是用什么类型来表示的。
程序集:System.Reflection.Emit.AssemblyBuilder(定义并表示动态程序集)
构造函数:System.Reflection.Emit.ConstructorBuilder(定义并表示动态类的构造函数)
自定义属性:System.Reflection.Emit.CustomAttributeBuilder(帮助生成自定义属性 使用构造函数传递的参数来生成类的属性)
枚举:System.Reflection.Emit.EnumBuilder(说明并表示枚举类型)
事件:System.Reflection.Emit.EventBuilder(定义类的事件)
字段:System.Reflection.Emit.FieldBuilder(定义并表示字段。无法继承此类)
局部变量:System.Reflection.Emit.LocalBuilder(表示方法或构造函数内的局部变量)
方法:System.Reflection.Emit.MethodBuilder(定义并表示动态类的方法(或构造函数))
模块:System.Reflection.Emit.ModuleBuilder(定义和表示动态程序集中的模块)
参数:System.Reflection.Emit.ParameterBuilder(创建或关联参数信息 如:方法参数,事件参数等)
属性:System.Reflection.Emit.PropertyBuilder(定义类型的属性 (Property))
类:System.Reflection.Emit.TypeBuilder(在运行时定义并创建类的新实例)
我们有了这些类型,基本上就可以动态创建我们的任何需要使用的类型,当然很多可以动态创建的类型我不可能都介绍完,如果在项目中有需要可以去查阅MSDN,里面都有DEMO的,主要的问题就是要理解每一种类型的定义,比如:程序集加载是靠AppDomain,程序集里包含多个模块,模块里可以声明类,类里可以创建方法、属性、字段。方法需要在类中才可以创建的,局部变量是声明在方法体内等等规则。看MSDN就非常容易弄懂了。
1.如何动态创建它们了
AppDomain:应用程序域(由 AppDomain 对象表示)为执行托管代码提供隔离、卸载和安全边界。AppDomain同时可以载入多个程序集,共同来实现功能。
程序集:简单来说就是一个以公共语言运行库(CLR)为宿主的、版本化的、自描述的二进制文件。(说明:定义来自C#与.NET3.5高级程序设计(第四版))
模块:类似于以前的单元,用于分割不同的类和类型,以及资源(resource, 资源记录就是字符串,图象以及其它数据,他们只在需要的时候才会被调入内存)。类型的Meta信息也是模块的一部分。多个模块组建成一个程序集。
所谓动态就是在程序运行时,动态的创建和使用。
直接看代码吧,其实超级简单。
//动态创建程序集 AssemblyName DemoName = new AssemblyName("DynamicAssembly"); AssemblyBuilder dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(DemoName, AssemblyBuilderAccess.RunAndSave); //动态创建模块 ModuleBuilder mb = dynamicAssembly.DefineDynamicModule(DemoName.Name, DemoName.Name + ".dll"); //动态创建类MyClass TypeBuilder tb = mb.DefineType("MyClass", TypeAttributes.Public); //动态创建字段 FieldBuilder fb = tb.DefineField("", typeof(System.String), FieldAttributes.Private); //动态创建构造函数 Type[] clorType = new Type[] { typeof(System.String) }; ConstructorBuilder cb1 = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, clorType); //生成指令 ILGenerator ilg = cb1.GetILGenerator();//生成 Microsoft 中间语言 (MSIL) 指令 ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldarg_1); ilg.Emit(OpCodes.Stfld, fb); ilg.Emit(OpCodes.Ret); //动态创建属性 PropertyBuilder pb = tb.DefineProperty("MyProperty", PropertyAttributes.HasDefault, typeof(string), null); //动态创建方法 MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName; MethodBuilder myMethod = tb.DefineMethod("get_Property", getSetAttr, typeof(string), Type.EmptyTypes); //生成指令 ILGenerator numberGetIL = myMethod.GetILGenerator(); numberGetIL.Emit(OpCodes.Ldarg_0); numberGetIL.Emit(OpCodes.Ldfld, fb); numberGetIL.Emit(OpCodes.Ret); //保存动态创建的程序集 dynamicAssembly.Save(DemoName.Name + ".dll"); <span style="white-space:pre"></span>
现在开始动态创建类:
构造函数:System.Reflection.ConstructorInfo(发现类构造函数的属性并提供对构造函数元数据的访问权)
事件:System.Reflection.EventInfo(发现事件的属性并提供对事件元数据的访问权)
字段:System.Reflection.FieldInfo(发现字段属性并提供对字段元数据的访问权)
方法:System.Reflection.MemberInfo(获取有关成员属性的信息并提供对成员元数据的访问)
成员:System.Reflection.MemberInfo(获取有关成员属性的信息并提供对成员元数据的访问)
参数:System.Reflection.ParameterInfo(发现参数属性并提供对参数元数据的访问)
属性:System.Reflection.PropertyInfo (发现属性 (Property) 的属性 (Attribute) 并提供对属性 (Property) 元数据的访问)
同样这是一种延伸阅读,只是先对这些进行了解,如果不知道的话,可能对动态的使用类型就无法下手了。
今天我做了一个Demo,先上Demo吧,然后在来解释程序是如何执行的。
//动态创建程序集 AssemblyName DemoName = new AssemblyName("DynamicAssembly"); AssemblyBuilder dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(DemoName, AssemblyBuilderAccess.RunAndSave); //动态创建模块 ModuleBuilder mb = dynamicAssembly.DefineDynamicModule(DemoName.Name, DemoName.Name + ".dll"); //动态创建类MyClass TypeBuilder tb = mb.DefineType("MyClass", TypeAttributes.Public); //动态创建字段 FieldBuilder fb = tb.DefineField("", typeof(System.String), FieldAttributes.Private); //动态创建构造函数 Type[] clorType = new Type[] { typeof(System.String) }; ConstructorBuilder cb1 = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, clorType); //生成指令 ILGenerator ilg = cb1.GetILGenerator();//生成 Microsoft 中间语言 (MSIL) 指令 ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldarg_1); ilg.Emit(OpCodes.Stfld, fb); ilg.Emit(OpCodes.Ret); //动态创建属性 PropertyBuilder pb = tb.DefineProperty("MyProperty", PropertyAttributes.HasDefault, typeof(string), null); //动态创建方法 MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName; MethodBuilder myMethod = tb.DefineMethod("get_Property", getSetAttr, typeof(string), Type.EmptyTypes); //生成指令 ILGenerator numberGetIL = myMethod.GetILGenerator(); numberGetIL.Emit(OpCodes.Ldarg_0); numberGetIL.Emit(OpCodes.Ldfld, fb); numberGetIL.Emit(OpCodes.Ret); //保存动态创建的程序集 dynamicAssembly.Save(DemoName.Name + ".dll");
执行的主要方法
//动态创建程序集 AssemblyName DemoName = new AssemblyName("DynamicAssembly"); AssemblyBuilder dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(DemoName, AssemblyBuilderAccess.RunAndSave); //动态创建模块 ModuleBuilder mb = dynamicAssembly.DefineDynamicModule(DemoName.Name, DemoName.Name + ".dll"); //动态创建类MyClass TypeBuilder tb = mb.DefineType("MyClass", TypeAttributes.Public); //动态创建字段 FieldBuilder fb = tb.DefineField("", typeof(System.String), FieldAttributes.Private); //动态创建构造函数 Type[] clorType = new Type[] { typeof(System.String) }; ConstructorBuilder cb1 = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, clorType); //生成指令 ILGenerator ilg = cb1.GetILGenerator();//生成 Microsoft 中间语言 (MSIL) 指令 ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldarg_1); ilg.Emit(OpCodes.Stfld, fb); ilg.Emit(OpCodes.Ret); //动态创建属性 PropertyBuilder pb = tb.DefineProperty("MyProperty", PropertyAttributes.HasDefault, typeof(string), null); //动态创建方法 MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName; MethodBuilder myMethod = tb.DefineMethod("get_Property", getSetAttr, typeof(string), Type.EmptyTypes); //生成指令 ILGenerator numberGetIL = myMethod.GetILGenerator(); numberGetIL.Emit(OpCodes.Ldarg_0); numberGetIL.Emit(OpCodes.Ldfld, fb); numberGetIL.Emit(OpCodes.Ret); //保存动态创建的程序集 dynamicAssembly.Save(DemoName.Name + ".dll");
我创建了一个类,类里创建了有一个字段、有一个参数的构造函数、一个属性、有一个参数的构造函数和一个方法。用有参数的构造函数来初始化字段myField,然后调用get_Field方法返回myField字段的值。控制台程序显示“Hello World!!!”
- C#笔记_1-动态生成类,程序集
- C# 动态生成程序集
- c#动态编译,动态生成程序集
- c#动态编译,动态生成程序集
- C#动态生成代码和程序集
- C# 初学笔记_1
- C# 动态生成类
- C# 动态生成类
- c#动态生成类
- C#学习笔记_1.C#概述
- C#反射、动态生成类
- C#动态加载程序集
- C# 动态加载程序集
- 笔记_1
- C#动态生成html
- C#动态生成gridview
- C#动态生成RadioButton
- C#动态生成XML
- 学生信息管理系统
- UIView的clipsToBounds属性和CALayer的masksToBounds属性的比较
- Swift,语法入门学习
- Codeforces Round #197 (Div. 2) -- C. Xenia and Weights (DFS回溯)
- Spring 入门知识储备
- C#笔记_1-动态生成类,程序集
- blog myBlog = new blog()
- Street
- 20160708关于Linux中MYSQL的密码修改
- 有些人为什么那么努力
- PhpStorm创建工程关联本地服务器目录
- echarts获取注册地图方法
- 适用于头像的圆形的ImageView
- android studio代码混淆注意问题