本来反射是跟SilverLight是没有必然联系的,之所以把这一篇随笔也冠以“SilverLight系列”的前缀,是因为这一篇里所讲的内容确实是在做SilverLight开发时才决心要做的。相信涉猎过SilverLight的人都会见过类似于如下的XAML代码:
<TextBox Text=”{Binding Path=[0].Name}” />
记得一开始看到"[0].Name”这种表达式时就觉得SilverLight里的绑定真是强大,而且也预感在接下来的SL的开发中,如果能使用类似于GetPropertyValue(“[0].Name”)这种方法的话是能够带来多大的方便,于是就决定要实现这一应用。实现起来包含以下几点思路:
1、定义成object类型的扩展方法,方便使用;
2、解析属性路径表达式(即[0].Name这种),获取有序的属性定义集合;
3、对于GetPropertyValue,根据解析而得的属性定义集合,通过循环获取的方式,取出最终值,如[0].Name,实际上应取集合中第一个元素的Name属性值;
4、对于SetPropertyValue,根据解析而得的属性定义集合,获取倒数第二个值,再设置最后一个属性值,如[0].Name,应先获取集合第一个元素,然后再设置这个元素的Name值;
5、暂不考虑复杂类型的索引器属性,即仅支持参数类型为int或string的索引器;
先自定义一个类用于标识一个属性:
private class PropInfo { /// <summary> /// 属性名 /// </summary> public string Name = ""; /// <summary> /// 是否为索引器属性 /// </summary> public bool IsIndexer = false; /// <summary> /// 索引器参数是否为整型 /// </summary> public bool IsNumberIndexer = false; }
由于不需要做任何赋值上的处理,所以并没应定义成属性的形式。之所以需要标明是否为整型,是因为通过PropertyInfo.GetValue方法来获取值时,需要指定参
数类型。
为了尽量减少重复的解析动作,使用一个静态成员将已解析过的属性路径缓存起来:
private static readonly Dictionary<string, List<PropInfo>> PropertyCache = new Dictionary<string, List<PropInfo>>();
然后便是用于检测属性路径合法性的正则表达式:
/// <summary> /// 检测属性路径字符串是否合法的正则表达式 /// </summary> private static readonly Regex RegPropertyPath = new Regex(@"(\d|\w|(\[(\d|\w)+\])|\.)+"); /// <summary> /// 检测是否为数字 /// </summary> private static readonly Regex RegNumber = new Regex(@"\d+");
解析属性路径表达式的算法很简单,按字符读取,做一些条件判断即可:
/// <summary> /// 解析属性路径字符串 /// </summary> /// <param name="propertyPath">将属性路径字符串解析为属性描述数组</param> /// <returns></returns> private static List<PropInfo> AnalyzePropertyPath(string propertyPath) { propertyPath = propertyPath + "$"; if (PropertyCache.ContainsKey(propertyPath)) return PropertyCache[propertyPath]; List<PropInfo> proInfoList = new List<PropInfo>(); PropInfo proInfo = null; for (int i = 0; i < propertyPath.Length; i++) { char c = propertyPath[i]; if (c == '[') { if (proInfo == null) { proInfo = new PropInfo() { IsIndexer = true }; } else { if (proInfo.Name == "") { throw new AnalyzePropertyException("analyze propertypath failed!"); } else { proInfoList.Add(proInfo); proInfo = new PropInfo(){ IsIndexer = true}; } } continue; } //属性结尾 if (c == ']' || c == '.') { if (proInfo != null) { if (proInfo.Name == "") { throw new AnalyzePropertyException("analyze propertypath failed, unknown property:[]"); } else { if (RegNumber.IsMatch(proInfo.Name)) proInfo.IsNumberIndexer = true; proInfoList.Add(proInfo); proInfo = null; } } continue; } //最后一个属性 if (c == '$' && proInfo != null) { proInfoList.Add(proInfo); break; } //累计属性名中的字符 if (proInfo == null) { proInfo = new PropInfo(); } proInfo.Name = proInfo.Name + c.ToString(); } PropertyCache.Add(propertyPath, proInfoList); return proInfoList; }
获取值跟设置值的方法我就不贴出来了,省得一篇随笔下来放眼过去全是代码。使用方法示例:
List<Person> persons = new List<Person>() { new Person() { Name = "lfx" } }; string name = (string)persons.GetPropertyValue("[0].Name"); //获取值 persons.SetPropertyValue("[0].Name", "hello lfx"); //设置值
完整代码请点击链接下载:ObjectExtention.rar