SilverLight系列——通过反射获取或设置指定属性路径表达式的值

      本来反射是跟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
 
posted @ 2011-08-16 21:00  细雨黄昏  阅读(846)  评论(2编辑  收藏  举报