动态类型与PropertyGrid

背景
  我在一个项目中遇到这样一件事。一开始用户对要编辑的数据
没有多少要求,于是我用PropertyGrid来提供编辑界面,我的开
发被大大简化了。但是用户使用了一段时间后提出,所有对象的
属性个数必须可以动态增减,甚至在运行中。虽然他们再次表示
增减的个数不会超过5个,但是这次我选择不相信他们了,我需要
一个具有一定弹性的设计。于是每个对象会自带一个Dictionary保
存属性。我再提供配置文件来描述每种对象的属性表。到目前为止
一切OK。但是当我将这个对象赋给PropertyGrid时问题来了,这个
控件竟然不允许手动增减属性,她只接受对象的public property!

问题
  如上所述
  1、我们有一个对象,对象要编辑的属性不是它自身的property,
  而是保存在一个Dictionary里;
  2、对象属性的编辑界面PropertyGrid只接受public property;
  3、我不想自己开发编辑界面;

分析
  根据需求我们不难看出真正困扰我的其实是第3条,而这一条是
我自己强加的,用户对编辑界面的唯一要求就是简单,
PropertyGrid他们认可,Label加TextBox他们也认可,在这方面他
们其实是很可爱的。
  我现在可以放弃我自己的需求实现一个Label加TextBox的对话框
,也可以用DataGridView。其实只要放弃PropertyGrid我有很多选
择。但是我喜欢PropertyGrid,他在这个项目中正合适,而且我自
己很难实现出它的效果。
  其实说白了,我们只要把Dictionary中间的键值对变成对象的
property就行了,所有的矛盾一下归结为:如何创建一个对象,它
的property都来自Dictionary中间的键值对。这时我的脑海中闪出
一个单词“Emit”。
  Emit是一个很强大的功能,但是不太好用(大概这是一个铁律:
强大的都不好驾驭),我也就没有认真学习过。现在机会来了。
我在这里只简单介绍一下Emit的使用,感兴趣的可以深入学习,最
好能把学习成果发布到这里与大家分享。

Emit简介
  Emit是一种允许代码在运行时创建并执行代码的功能,用它创建
的代码功能有限制,但是执行效率与编译后的代码无异。提醒一下
,想使用Emit需要对DotNet的内存模型有一定的了解,更重要的是
需要了解IL的知识。下面我用一个例子说明使用Emit的大致流程,
一个HelloWorld程序;)

(这段代码来自项目exam/exam1)

Code



  上面的代码先创建了一个程序集“DynamicAssembly”,然后给程序集增加
一个模块“DynamicModule”,最后在模块中创建类型“DynamicClass”。这是
使用Emit的经典步骤。在得到类型以后真正的工作才开始。上面的代码只
是为类型定义了一个函数“Greet”,然后用ILGenerator来为函数注入代码:
Console.WriteLine("HelloWorld")。

 

 好了,Emit就介绍到这里。下面我们来看Emit技术实现我们的动态属性对象。

动态属性对象实现
  我们需要的是一个可以把一系列键值对转换为一个类型的public property的
工具,说白了就是一个函数。
  关于这个动态类型,我们得仔细考虑:
  1)它的public property都是来自输入的键值对;
  2)每个property名称都是键的名称;
  3)每个property都有get和set函数;
  4)我们需要在set函数里面发出修改前事件(prevSet)和修改完成事件(postSet)。
最后一条又是我加的,因为我需要在属性值被修改前对新值进行检验,修改
后提示有关界面(也为实现诸如MVP之类的模式提供支持)。
  这里的代码进行了简化,我们只处理属性值是字符串的情况。也没有(UITypeEditor)。

这样做是为了不分散大家注意力。

  我们已经知道要创建一个什么样的动态类型了,现在来看代码:

(这段代码来自项目exam2/utilities.cs)

Code


   
  这段代码有很详细的注释,我就不废话了。
代码下载:demo.zip
后话:
  这里提供的代码,可以随意修改使用。只是希望各位将自己的心得,收获和困扰放到这里与大家分享。

 

你可能感兴趣的:(property)