const、static、readonly 一直以来勇哥都没有完全搞明白其区别。正好手里有项目用到static readonly,因此仔细研究了一翻。记录如下:
static readonly myclass1 c1 = new myclass1();
这里的初始化在定义时初始化,如果放在其它位置则报错。
const int myPI 跟常量是一样的,你可以必须使用常量的地方直接使用,比如下面的case中。这种常量变量是在编译时就确定了。
static readonly int 这个是在运行时计算出其值的,所以还能通过静态构造函数来赋值
有一种情况,无法使用const来定义常量,而又需要它是只读的量。
例如下面的情况:
public class Color { public static readonly Color Black = new Color(0, 0, 0); public static readonly Color White = new Color(255, 255, 255); public static readonly Color Red = new Color(255, 0, 0); public static readonly Color Green = new Color(0, 255, 0); public static readonly Color Blue = new Color(0, 0, 255); private byte red, green, blue; public Color(byte r, byte g, byte b) { red = r; green = g; blue = b; } }
这个时候如果你用const定定义,则报错:
原因很简单,const定义的变量是必须在编译中确定的量。 而new Color(22,22,22)是在运行时才能确定的量
如果你对Black重新赋值,会报错“无法对只读的静态变量赋值”
Color1.Black = new Color1(33, 33, 33);
但是对于
static readonly myclass1 c1 = new myclass1();
你可以使用它的add, del, update方法对内部的静态只读staticreadlyobj列表进行修改。
//应该以类名方式访问
c1.x2 = 66;
//只读,不可赋值
myclass1.x2 = 77;
因此,勇哥总结一下:
C# 中的 readonly 关键字表示类中的字段只允许在定义时候或者构造方法中初始化。普通类型的数据完全可以达到预期的效果,但是在对象或者列表中,要想达到只读的效果,只用一个 readonly 关键字是不可以的。当你把一个 List 用 readonly 修饰,在其他类中仍然可以使用 Add,Remove 方法来改变它。但是可能你想要的只读属性是:只有在当前类中修改这个列表的 item,才不想被其他类做任何修改!
这样的设计,感觉是不是有点奇怪呢?
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApp1 { public partial class Form1 : Form { const int myPI = 3; static readonly int myPI2 = 5; static readonly myclass1 c1 = new myclass1(); public Form1() { InitializeComponent(); //无法对静态只读字段赋值 //c1 = new myclass1(); } private void button1_Click(object sender, EventArgs e) { c1.x1 = myPI; switch (c1.x1) { //错误,应该输入常量 //case myPI2: // break; case 11: break; case myPI: Debug.WriteLine(myPI); break; } //可以对类的静态只读字段进行添加删除数据 myclass1 s1 = new myclass1(); s1.add(5); myclass1 s2 = new myclass1(); s2.add(6); s2.del(5); s1.update(6, 7); for (int i = 0; i < myclass1.staticreadlyobj.Count; i++) { Debug.WriteLine(myclass1.staticreadlyobj[i]); } Debug.WriteLine("(1)--------"); c1.add(22); c1.add(33); c1.del(33); myclass1.staticreadlyobj.ForEach(s => Debug.WriteLine(s)); c1.x1 = 55; //应该以类名方式访问 //c1.x2 = 66; //只读,不可赋值 //myclass1.x2 = 77; //无法对只读的静态变量赋值 // Color1.Black = new Color1(33, 33, 33); s1.x3[0] = 300; foreach(var m in s1.x3) { m = 400; } s1.x3 = new List<int>() { 2, 3, 4 }; s1.x3.Add(33); s1.x3.Remove(4); } } public class myclass1 { public static readonly List<int> staticreadlyobj = new List<int>() { 1, 2, 3, 4 }; public int x1 { get; set; } = 0; //static readonly 对属性无效 //public static readonly int x2{get;set;}=0; public static readonly int x2=0; private readonly List<int> _x3; public IEnumerable<int> x3 { get { return _x3; } } public myclass1() { _x3 = new List<int>() { 100, 101, 102 }; } public void add(int data) { staticreadlyobj.Add(data); } public void del(int data) { staticreadlyobj.Remove(data); } public void update(int data, int newdata) { for (int i = 0; i < staticreadlyobj.Count; i++) { if (staticreadlyobj[i] == data) { staticreadlyobj[i] = newdata; return; } } } } public class Color1 { public static readonly Color1 Black = new Color1(0, 0, 0); public static readonly Color1 White = new Color1(255, 255, 255); public static readonly Color1 Red = new Color1(255, 0, 0); public static readonly Color1 Green = new Color1(0, 255, 0); public Color1 Blue = new Color1(0, 0, 255); private byte red, green, blue; public Color1(byte r, byte g, byte b) { red = r; green = g; blue = b; } } }
那么如果我们希望一个static readonly的List完全只读,不可以添加删除,怎么办呢?
有两个办法:
一是使用IEnumerable<T>代替List<T>,如下:
private readonly List<int> _x3; public IEnumerable<int> x3 { get { return _x3; } }
因为IEnumerable<T>没有Add, Remove方法。
因此下面的任何企图修改列表值的意图都是失败的。
另个一种简单办法是,使用 .NET 4.5 中,List<T> 继承了 IReadOnlyList<T> 和 IReadOnlyCollection<T>,给了我们一种更简单的写法,同样可以达到上述效果。
勇哥最后说说为啥会写下此文。因为项目中有这样一个信号量:
其实不能写成划线处这样,而是要写样写:
private static readonly ManualResetEvent[] mr = new ManualResetEvent[2];
如果不是static, 只有readonly,则会出现随机的错误。
现象是在程序的一个方法内做mr.set()之后,在另一个方法内mr.WaitOne(); 会发现程序卡住了。
但是你还会发现如果在这句mr.WaitOne()的前面一句加上mr.set()则是正常的。但是mr.set()分散到其它方法则无效。
而且这种现象还可能出现,也可能不出现。
晕死。
因此,请确保你的信号量一定要是static readonly。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

