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
好了,说了那么多,相信朋友们对反射的概念有了一个比较深刻的认识了,推荐有时间的朋友按照我上面的代码去实现一次,以便更深的掌握这些内容。
浙公网安备 33010602011771号