C#强化系列文章五:动态代码的使用(反射和动态生成类)

在软件开发尤其是框架和底层开发时,为了更灵活的控制代码,常常需要进行一些动态的操作。比如根据用户的输入等动态的调用类中的方法或者根据数据库表结构、用户要求动态的生成一些类,然后再动态的调用类中的方法。当然使用这些方式时会对性能有一点影响,具体使用过程中可以根据实际情况来定,不过一般的B/S开发中主要的瓶颈还是在数据库操作和网速方面,这点影响应该可以忽略的
下面我就从这两个方面来说说动态代码的使用:
一、反射的使用
可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
需要使用的命名空间:System.Reflection
反射的作用很多,下面的例子主要是看一下怎么动态的调用类中的方法。
例子类
这个例子中提供了三个方法和一个属性,下面的代码来动态的调用它们:
            string strText = "abcd";

            BindingFlags flags 
= (BindingFlags.NonPublic | BindingFlags.Public |
                BindingFlags.Static 
| BindingFlags.Instance | BindingFlags.DeclaredOnly);

            Type t 
= typeof(ReflTest1);
            MethodInfo[] mi 
= t.GetMethods(flags);
            Object obj 
= Activator.CreateInstance(t);

            
foreach (MethodInfo m in mi)
            
{
                
if (m.Name.StartsWith("Write"))
                
{
                    m.Invoke(obj, 
new object[] { strText });
                }

            }


            MethodInfo mMy 
= t.GetMethod("MyWrite");
            
if (mMy != null)
            
{
                mMy.Invoke(obj, 
new object[] { strText });
            }

BindingFlags用来设置要取得哪些类型的方法,然后我们就可以取得这些方法来动态的调用。(当然为了可以循环的调用方法,在方法的命名方面可以自己指定一个规则)

二、动态生成类
我们可以在程序运行过程中调用.NET中提供的编译类,动态的将一段string编译成一个类,然后再通过反射来调用它
需要使用的命名空间:System.CodeDom System.CodeDom.Compiler Microsoft.CSharp System.Reflection
动态创建、编译类的代码如下:
        public static Assembly NewAssembly()
        
{
            
//创建编译器实例。   
            provider = new CSharpCodeProvider();
            
//设置编译参数。   
            paras = new CompilerParameters();
            paras.GenerateExecutable 
= false;
            paras.GenerateInMemory 
= true;

            
//创建动态代码。   
            StringBuilder classSource = new StringBuilder();
            classSource.Append(
"public   class   DynamicClass \n");
            classSource.Append(
"{\n");

            
//创建属性。   
            classSource.Append(propertyString("aaa"));
            classSource.Append(propertyString(
"bbb"));
            classSource.Append(propertyString(
"ccc"));

            classSource.Append(
"}");

            System.Diagnostics.Debug.WriteLine(classSource.ToString());

            
//编译代码。   
            CompilerResults result = provider.CompileAssemblyFromSource(paras, classSource.ToString());

            
//获取编译后的程序集。   
            Assembly assembly = result.CompiledAssembly;

            
return assembly;
        }


        
private static string propertyString(string propertyName)
        
{
            StringBuilder sbProperty 
= new StringBuilder();
            sbProperty.Append(
" private   int   _" + propertyName + "   =   0;\n");
            sbProperty.Append(
" public   int   " + "" + propertyName + "\n");
            sbProperty.Append(
" {\n");
            sbProperty.Append(
" get{   return   _" + propertyName + ";}   \n");
            sbProperty.Append(
" set{   _" + propertyName + "   =   value;   }\n");
            sbProperty.Append(
" }");
            
return sbProperty.ToString();
        }
propertyString方法就是用来拼写字符串的
整个代码比较简单,主要步骤就是:1、拼写类的字符串  2、调用CSharpCodeProvider类进行编译得到程序集(assembly)

接下来就可以利用之前反射的方法来动态调用这个类中的属性了:
            Assembly assembly = NewAssembly();

            
object Class1 = assembly.CreateInstance("DynamicClass");
            ReflectionSetProperty(Class1, 
"aaa"10);
            ReflectionGetProperty(Class1, 
"aaa");

            
object Class2 = assembly.CreateInstance("DynamicClass");
            ReflectionSetProperty(Class1, 
"bbb"20);
            ReflectionGetProperty(Class1, 
"bbb");
DynamicClass是我动态类的类名,aaa和bbb是其中的属性
ReflectionSetProperty和ReflectionGetProperty代码如下:
给属性赋值

取得属性的值


posted @ 2008-03-07 10:41 永春 阅读(10207) 评论(28) 编辑 收藏

 回复 引用 查看   
#1楼 2008-03-07 12:11 蓝天旭日      
System.Reflection
......

 回复 引用   
#2楼 2008-03-07 12:25 reader[未注册用户]
不错,学习了
 回复 引用 查看   
#3楼 2008-03-07 13:32 Allie      
第一次看到动态类啊。。虽然目前还不知道用处!感谢博主~!
 回复 引用 查看   
#4楼 2008-03-07 14:31 狼Robot      
属性的赋值应该可以这样写:
PropertyInfo _Property = objClass.GetType().GetProperty(propertyName);
if (_Property != null && _Property.CanRead)
{
_Property.SetValue(objClass, value, null);
}

取得应该可以这样写:
PropertyInfo _Property = objClass.GetType().GetProperty(propertyName);
if (_Property != null && _Property.CanWrite)
{
_Property.GetValue(objClass, null);
}

这样就可以不用foreach了
这只是我的看法,不知道这样效率是不是会高些...

 回复 引用 查看   
#5楼[楼主] 2008-03-07 14:42 永春      
@狼Robot
你的写法的确可以提高效率-_-

我的例子中这两个方法是从其他项目中拷过来修改的,没有考虑效率问题

 回复 引用 查看   
#6楼 2008-03-07 18:03 老钱      
up

 回复 引用   
#7楼 2008-03-07 18:37 zzz[未注册用户]
_Property.CanRead)
{
_Property.SetValue ???

.CanWrite)
{
_Property.GetValue????

晕死

 回复 引用 查看   
#8楼 2008-03-07 23:46 小瑞克      
学习了,我正在找这方面的文章
 回复 引用   
#9楼 2008-03-08 09:28 东[未注册用户]
多谢分享
 回复 引用 查看   
#10楼[楼主] 2008-03-08 15:11 永春      
@zzz
@狼Robot
CanRead和CanWrite是写反调了-_-

 回复 引用 查看   
#11楼 2008-06-02 10:21 mickeysuda      
动态生成类不错。。。
 回复 引用 查看   
#12楼 2008-06-03 16:56 张荣华      
动态生成类 不错 不过效率好象会很低吧 博主知道应用场合吗?:)

 回复 引用 查看   
#13楼[楼主] 2008-06-04 19:37 永春      
我想一般都是在写框架的时候用到,初始化的时候生成,然后缓存起来以后使用就可以了
 回复 引用   
#14楼 2008-07-06 16:36 傅军[未注册用户]
"我想一般都是在写框架的时候用到",这句话不是非常懂,能否详细说一下。
我也不大理解动态生成类的具体使用场合。

 回复 引用 查看   
#15楼[楼主] 2008-07-07 12:35 永春      
@傅军
比如写一个开发框架,动态的根据表来生成动态的类使用

 回复 引用   
#16楼 2008-07-07 13:24 傅军[未注册用户]
恩,目前没有使用过,也没有看到具体的例子,还是不大好理解。
感觉和利用反射一样,让系统在运行时再去决定一些东西。
谢谢你,以后等用到了应该就好理解了:)

 回复 引用 查看   
#17楼 2008-08-27 17:00 番茄爱炒蛋      
我想能不能根据这个来做个编译器呢
比如从一个cs文件中读取代码 删减去命名空间外面的代码
然后编译

 回复 引用 查看   
#18楼 2008-09-12 11:15 飛雪飄寒      
我在写WebServices接口时会经常用到反射。
 回复 引用   
#19楼 2008-10-31 16:11 俄裸死方块[未注册用户]
此动态生成 效率很低。
建议使用 EMIT 进行的动态类的生成。

 回复 引用   
#20楼 2009-04-22 12:52 三大赛[未注册用户]
动态生成了程序集,将会被加载到当前的应用程序域中,如果代码中有很多这样的动态加载,是不是程序集也很多呢?有没有办法在不使用的情况之下动态卸载、
 回复 引用   
#21楼 2009-06-01 09:13 xxxholic[未注册用户]
很好很强大,赞个!!
 回复 引用   
#22楼 2009-08-27 15:46 wo ye shi[未注册用户]
引用飛雪飄寒:我在写WebServices接口时会经常用到反射。

--------------------
我也是,很有用!

 回复 引用 查看   
#23楼 2010-11-11 15:29 风尘浪子      
公司要开发一套代码生成系统,多谢指教
 回复 引用 查看   
#24楼 2011-03-06 09:13 fqApollo      
请问,我可以在已存在的程序集里生成这个新的类,而不是一个新的程序集么?
 回复 引用 查看   
#25楼 2011-08-16 17:58 亚历山大兄弟      
通用编辑器框架