在工作当中,经常会用到反射技术来实现对一些对象的序列化\反序列化的功能。
以下是对于FieldInfo这个类型的两点心得:
假设有如下结构
public struct SomeStruct { public int publicField; private int privateField; public static int staticField; }
一、使用BindingFlags获取一个类或者结构特定的字段
以下是一段测试代码:
private static void TestGetFields() { Action<IEnumerable<FieldInfo>> display = (e) => { foreach (var item in e) { Console.WriteLine(item.Name); } }; // display fields SomeStruct ss = new SomeStruct(); Console.WriteLine("BindingFlags.Instance | BindingFlags.Public"); var fields = ss.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); display(fields); Console.WriteLine("BindingFlags.Static | BindingFlags.Public"); fields = ss.GetType().GetFields(BindingFlags.Static | BindingFlags.Public); display(fields); Console.WriteLine("BindingFlags.Instance | BindingFlags.NonPublic"); fields = ss.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic); display(fields); }
得到的结果如下:
以上结果似乎在意料当中,但是有一点需要注意的是,在设定BingdingFlags的时候,必须使用BingFlags.Instande或者BindingFlags.Static,否则GetFields方法将返回一个空集合。
二、结构对象使用FieldInfo.SetValue方法
对一个结构使用SetValue方法是需要小心的。
FieldInfo.SetValue方法的函数签名如下:
public void SetValue(object obj, object value);
看下面的两个方法:
private static void FieldInfo_SetValue1() { SomeStruct ss = new SomeStruct(); FieldInfo fieldInfo = ss.GetType().GetField("publicField"); fieldInfo.SetValue(ss, 20); Console.WriteLine(ss.publicField); // result is 0; } private static void FieldInfo_SetValue2() { SomeStruct ss = new SomeStruct(); object obj = ss as object; // boxing ss; FieldInfo fieldInfo = ss.GetType().GetField("publicField"); fieldInfo.SetValue(obj, 20); ss = (SomeStruct)obj; // unboxing ss; Console.WriteLine(ss.publicField); // result is 20; }
我们知道第一种方法会有问题,原因就在于调用SetValue方法的时候,由于结构对象是值类型,因此会临时被装箱,造成实际修改的字段不是ss的字段;第二种方法就是先装箱,得到一个对于ss备份的引用,然后修改完成后在拆箱赋值给ss。