• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

IT-哲

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

C#的嚣张机制-Reflection(反射)

首先来讲讲什么是反射: 这是.Net中获取运行时类型信息的方式

Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。 
MethodInfo包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。 
诸如此类,还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。 

总之就是一句话,反射就是我想看看这个类里有什么,并且去使用它。

先给大家举一个我做的简单的例子,看看反射的应用。

我想要做一个画板,上面有不同的颜色,而且标注着该颜色的名字。

如图:

可以看到 这个画板包含了好多的颜色,而这些颜色都不是我自己定义的,是通过反射System.Windows.Media.Brushes类来实现的。

现在我们看一下WPF的Behind-Code:

 public class ColorGridBox : ListBox
    {
        PropertyInfo[] prop = typeof(Brushes).GetProperties();        
        public ColorGridBox()
        {
            FrameworkElementFactory factory = new FrameworkElementFactory(typeof(UniformGrid));
            factory.SetValue(UniformGrid.ColumnsProperty,0);
            ItemsPanel = new ItemsPanelTemplate(factory);
            foreach (PropertyInfo pro in prop)
            {
                StackPanel panel = new StackPanel();
                panel.VerticalAlignment = VerticalAlignment.Center;
                panel.HorizontalAlignment = HorizontalAlignment.Center;
                Rectangle rect = new Rectangle();
                rect.Width =85;
                rect.Height = 85;
                rect.Margin = new Thickness(1);
                rect.Fill = (Brush)typeof(Brushes).GetProperty(pro.Name).GetValue(null,null);
                
                panel.Children.Add(rect);
                Label lab = new Label();
                lab.Content = pro.Name;
                lab.Width = 85;
                lab.VerticalContentAlignment = VerticalAlignment.Center;
                lab.HorizontalContentAlignment = HorizontalAlignment.Center;
                lab.VerticalAlignment = VerticalAlignment.Center;
                lab.HorizontalAlignment = HorizontalAlignment.Center;
                
               // lab.Margin = new Thickness(1);
                panel.Children.Add(lab);
                this.Items.Add(panel);

                ToolTip top = new ToolTip();
                top.Content = pro.ToString();
                rect.ToolTip = top;
            }
            this.SelectedValuePath = "Children/Fill";
        }
    }

代码量很大,但是其实我们关心的只有三行。

 PropertyInfo[] prop = typeof(Brushes).GetProperties();    

表示获取一个属性集合,通过反射Brushes类来实现(brushes类的所有属性都是颜色,以实现画刷的功能)

  foreach (PropertyInfo pro in prop)
            {
              。
              。
              。
                rect.Fill = (Brush)typeof(Brushes).GetProperty(pro.Name).GetValue(null,null);
                
                panel.Children.Add(rect);
                Label lab = new Label();
                lab.Content = pro.Name;
               。
               。
               。
            }

遍历我所获取是属性集合,然后给矩形填充响应的颜色,GetValue获取属性的对应值,并转换为Brush类型。

最后把label的内容赋值为属性名(颜色对应的系统名称)。

这样,画板就完成了,反射给我们带来的优势则是我不需要一个一个的去定义每一个颜色,而是直接使用了Brushes类里面定义好的颜色属性。

跟上面说的一样,反射就是看看里面有什么,再去使用它。

当然反射的应用不可能仅仅是这种层面,还有更高层次上的应用。

当我们声明了一个类,里面定义了很多的私有方法和私有属性,现在我们调用这个类的时候,通过程序需要,需要访问并修改私有属性,调用私有方法,这该怎么办。

可能会有人告诉我,这是不可能的,C#定义的private修饰符就是基于这点考虑的。

其实通过反射,我们一样可以违背这个真理。

现在我们定义了一个类

  public class Cat
    {
        public Cat()
        {
            Name2 = "MyCat";
        }
        private string Name { get; set; }
        private string Name2 { get; set; }
        private string Name3 { get; set; }
        private string Name4 { get; set; }

        private void GetName()
        {
            Console.WriteLine(Name2);
        }
    }

这个类有4个私有属性,和一个私有方法,我在构造函数中,给Name2赋值为MyCat。

首先第一个问题,GetName是私有的,我如何通过实例化的对象去访问这个方法?

 Cat obj = new Cat();
  obj.GetType().GetMethod("GetName", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(obj, null);
            Console.ReadKey();
BindingFlags是类型搜索方法的标志,也就是说我们的搜索范围定义在实例成员与非公有成员内,我们的GetName方法自然包括在内,所以第一个参数写GetName,是可以搜索到该方法的,
然后通过Invoke方法调用该实例方法。

 

结果出来了,输出为MyCat。

同样 如何通过字段名或属性名访问私有的变量呢?

  typeof(Cat).GetProperty("Name2", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(obj, "Cat",null);

同样通过反射机制,可以给Obj对象的Name2私有成员设置一个值,为Cat

这次输出的结果就是Cat

同时,我们可以通过修改字段的属性去修改Name2的值,而我们通过 { get; set; }初始化器声明的属性,字段名是未知的,而我们通过断点调试,找到了隐藏字段的命名方式

 obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic).ToList();

然后我们编写代码,获取所有的私有字段,并对字段的值进行设置

            obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic).ToList().ForEach(r =>
            {
                if (r.Name == "<Name2>k__BackingField")
                {
                    list.Add(r.Name.ToString());
                }
            });
          obj.GetType().GetField(list[0], BindingFlags.Instance | BindingFlags.NonPublic).SetValue(obj, "Cat");

效果一样,我们通过修改一个属性的关系字段的值,也可以去改变该属性的值。

输出结果同样是Cat

好了,说了那么多,相信朋友们对反射的概念有了一个比较深刻的认识了,推荐有时间的朋友按照我上面的代码去实现一次,以便更深的掌握这些内容。

posted on 2013-04-16 14:07  IT-哲  阅读(525)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3