【读书笔记】《框架设计(第2版)CLR Via C#》中两个比较有趣的知识点(转)(good)
2010-05-31 22:11
561 查看
转载自:/article/6185788.html
常量:常量就是指永远不会改变的符号,在.Net通过编译以后常量的值会插入到程序集的元数据中,所以常量的类型必须是.Net的基元类型Boolean,Char,Byte,SByte,Int16,Int32,UInt32,Int64,UInt64,Singe,Double,Decimal和String。也就是说在一个程序集A的类中定义一个常量,在另一个程序集B中使用这个常量,当两个程序集编译以后,C#编译器会将这个常量插入到使用这个常量的程序集B的元数据中,而这时使用常量的程序集A对定义常量的程序集B没有运行时的依赖关系了,也就是说在运行时可以删除定义常量的程序集A,同时也说明如果修改这个常量的值再去编译定义常量的程序集A不会改变使用常量的程序集B在运行时获取到的这个常量的值,常量在带来这种好处的同时显然对于程序集的版本控制是很不利的。如果一个程序集需要从另一个程序集中总是获得最新的数据,则不能使用常量,这时可以使用只读字段。此外,常量具有static的含义,只能通过类型访问,不能通过对象访问。
只读字段:只读字段也是在初始化(这个初始化是在运行时初初始化的)以后在验证过程中不允许修改的字段,只读字段引用的可以是任何类型的对象。但要注意只读字段只有在对象初始化时可以给这个字段赋值,也就是字段在初始化时还具有可写属性,以后这个字段就只读了,字段的只读含义是这个变量的引用不可以改变了,但是具体引用的对象的状态时可以改变的。编译器和验证机制确保只读字段不会被任何其他方法写入,但是我们依然可以使用反射修改只读字段。只读字段属于对象实例的,需要通过对象访问,属于对象状态的一部分。
静态字段:静态字段是与类型关联的成员(只能通过类型访问,不像Java还可以通过类的实例访问,如果没记错的话),静态字段的初始化是在类型被加载到CLR中时执行的。静态字段可以应用任何类型的对象。
静态只读字段:静态只读字段与常量比较类似,但是静态只读字段不限于基元类型,可以是.Net中的任何类型,静态只读字段不像常量是在编译时插入到元数据中的,而静态只读字段是在运行时赋值和确定的,也就是对于程序的依赖是一种强依赖关系,具体的值是在运行时去程序集中取到的,而不像常量做一个拷贝插入元数据中。静态只读字段属于类型的,通过类型访问。
通过反射修改私有字段,下面一个示例通过反射修改私有的一般字段,常量,只读字段,静态字段,静态只读字段并打印出结果。在这里可以可以看出反射是很强大的,既然可以修改私有的只读字段,但要注意实际上通过反射也无法修改常量,修改会报错,这与常量的存储方式有关。通过反射给人一种可以跳过验证规则的假象。示例代码:
运行结果:
从这个结果可以看出,通过反射可以修改私有的普通字段,可以修改只读字段,可以修改静态字段,修改这三个都没有问题,但是不可以修改常量字段。当通过反射修改静态只读字段时(有点类似常量)时,可以正常执行,但是通过类获取的静态只读字段并没有改变,说明实际并没有修改成功,我们实际修改的只是通过反射获取的静态只读字段,并没有实际映射回去。具体原因感兴趣的同学可以通过IL分析一下,鄙人不才,不会IL。
常量:常量就是指永远不会改变的符号,在.Net通过编译以后常量的值会插入到程序集的元数据中,所以常量的类型必须是.Net的基元类型Boolean,Char,Byte,SByte,Int16,Int32,UInt32,Int64,UInt64,Singe,Double,Decimal和String。也就是说在一个程序集A的类中定义一个常量,在另一个程序集B中使用这个常量,当两个程序集编译以后,C#编译器会将这个常量插入到使用这个常量的程序集B的元数据中,而这时使用常量的程序集A对定义常量的程序集B没有运行时的依赖关系了,也就是说在运行时可以删除定义常量的程序集A,同时也说明如果修改这个常量的值再去编译定义常量的程序集A不会改变使用常量的程序集B在运行时获取到的这个常量的值,常量在带来这种好处的同时显然对于程序集的版本控制是很不利的。如果一个程序集需要从另一个程序集中总是获得最新的数据,则不能使用常量,这时可以使用只读字段。此外,常量具有static的含义,只能通过类型访问,不能通过对象访问。
只读字段:只读字段也是在初始化(这个初始化是在运行时初初始化的)以后在验证过程中不允许修改的字段,只读字段引用的可以是任何类型的对象。但要注意只读字段只有在对象初始化时可以给这个字段赋值,也就是字段在初始化时还具有可写属性,以后这个字段就只读了,字段的只读含义是这个变量的引用不可以改变了,但是具体引用的对象的状态时可以改变的。编译器和验证机制确保只读字段不会被任何其他方法写入,但是我们依然可以使用反射修改只读字段。只读字段属于对象实例的,需要通过对象访问,属于对象状态的一部分。
静态字段:静态字段是与类型关联的成员(只能通过类型访问,不像Java还可以通过类的实例访问,如果没记错的话),静态字段的初始化是在类型被加载到CLR中时执行的。静态字段可以应用任何类型的对象。
静态只读字段:静态只读字段与常量比较类似,但是静态只读字段不限于基元类型,可以是.Net中的任何类型,静态只读字段不像常量是在编译时插入到元数据中的,而静态只读字段是在运行时赋值和确定的,也就是对于程序的依赖是一种强依赖关系,具体的值是在运行时去程序集中取到的,而不像常量做一个拷贝插入元数据中。静态只读字段属于类型的,通过类型访问。
通过反射修改私有字段,下面一个示例通过反射修改私有的一般字段,常量,只读字段,静态字段,静态只读字段并打印出结果。在这里可以可以看出反射是很强大的,既然可以修改私有的只读字段,但要注意实际上通过反射也无法修改常量,修改会报错,这与常量的存储方式有关。通过反射给人一种可以跳过验证规则的假象。示例代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; namespace FalseVerification { class Program { static void Main(string[] args) { Type type = typeof(SomeType); SomeType st = new SomeType(); st.Print(); // 正常字段,当然可以修改 FieldInfo fi = type.GetField("f1", BindingFlags.NonPublic | BindingFlags.Instance); fi.SetValue(st, (Int32)fi.GetValue(st) + 1); // 常量字段,反射也无法修改,如果取消下面语句的注释,执行会出错 /* 原因说明:常量的值必须在编译时就确定(只能是基元类型),也就是说在定义时就赋值。 编译后常量的值是保存在程序集的元数据中,在运行时是不可修改的; 而其它字段是存储在动态内存中,在运行时是可修改的。*/ fi = type.GetField("f2", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); //fi.SetValue(null, (Int32)fi.GetValue(null) + 1); // 只读字段,可通过反射方式修改值 fi = type.GetField("f3", BindingFlags.NonPublic | BindingFlags.Instance); fi.SetValue(st, (Int32)fi.GetValue(st) + 1); // 静态字段,也可修改 fi = type.GetField("f4", BindingFlags.NonPublic | BindingFlags.Static); fi.SetValue(null, (Int32)fi.GetValue(null) + 1); // 静态只读字段,下面代码不出错,改变了反射字段的值,但类中的字段值并没有被改变 fi = type.GetField("f5", BindingFlags.NonPublic | BindingFlags.Static); fi.SetValue(null, (Int32)fi.GetValue(null) + 1); Int32 f5 = (Int32)fi.GetValue(null); // i5 得到值为51 st.Print(); Console.WriteLine("f5: " + f5.ToString()); Console.ReadKey(); } } public class SomeType { private Int32 f1 = 30;// 私有字段 private const Int32 f2 = 10;// 私有常量字段 private readonly Int32 f3 = 20;// 私有只读字段 private static Int32 f4 = 40;// 私有静态字段 private static readonly Int32 f5 = 50;// 私有静态只读字段 public void Print() { Console.WriteLine("f1: " + f1.ToString()); Console.WriteLine("f2: " + f2.ToString()); Console.WriteLine("f3: " + f3.ToString()); Console.WriteLine("f4: " + SomeType.f4.ToString()); Console.WriteLine("f5: " + SomeType.f5.ToString()); Console.WriteLine(); } } }
运行结果:
从这个结果可以看出,通过反射可以修改私有的普通字段,可以修改只读字段,可以修改静态字段,修改这三个都没有问题,但是不可以修改常量字段。当通过反射修改静态只读字段时(有点类似常量)时,可以正常执行,但是通过类获取的静态只读字段并没有改变,说明实际并没有修改成功,我们实际修改的只是通过反射获取的静态只读字段,并没有实际映射回去。具体原因感兴趣的同学可以通过IL分析一下,鄙人不才,不会IL。
相关文章推荐
- 【读书笔记】《框架设计(第2版)CLR Via C#》中两个比较有趣的知识点(转)(good)
- 【读书笔记】《框架设计(第2版)CLR Via C#》中两个比较有趣的知识点
- 『框架设计(第2版)CLR Via C#』学习笔记(001)——将源代码编译成托管模块
- 框架设计(第2版)CLR Via C#
- 框架设计(第2版):CLR Via C#
- 『框架设计(第2版)CLR Via C#』学习笔记(002)——将托管代码合并到程序集
- 框架设计(第2版):CLR Via C#
- 框架设计(第2版):CLR Via C#
- 『框架设计(第2版)CLR Via C#』学习笔记——CLR寄宿
- 『框架设计(第2版)CLR Via C#』学习笔记——使用is和as操作符来进行强制类型转换
- 『框架设计(第2版)CLR Via C#』学习笔记——常量
- 一网友推荐的书:框架设计(第2版):CLR Via C#
- 框架设计(第2版)CLR Via C#(1)
- clr via c#(框架设计)第3版读书笔记
- 枚举学习文摘 — 框架设计(第2版) CLR Via C#
- 框架设计(第2版):CLR Via C#
- 枚举学习文摘 — 框架设计(第2版) CLR Via C#
- 框架设计( CLR via C# 2nd Edition ) 读后感
- CLR via C# 3 读书笔记(6):第1章 CLR执行模型 — 1.6 框架类库
- CLR Via C#(框架设计)学习笔记 总结