您的位置:首页 > 编程语言 > C#

【读书笔记】《框架设计(第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中的任何类型,静态只读字段不像常量是在编译时插入到元数据中的,而静态只读字段是在运行时赋值和确定的,也就是对于程序的依赖是一种强依赖关系,具体的值是在运行时去程序集中取到的,而不像常量做一个拷贝插入元数据中。静态只读字段属于类型的,通过类型访问。

  通过反射修改私有字段,下面一个示例通过反射修改私有的一般字段,常量,只读字段,静态字段,静态只读字段并打印出结果。在这里可以可以看出反射是很强大的,既然可以修改私有的只读字段,但要注意实际上通过反射也无法修改常量,修改会报错,这与常量的存储方式有关。通过反射给人一种可以跳过验证规则的假象。示例代码:

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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: