C# const, static readonly的测试

const、static、readonly 一直以来勇哥都没有完全搞明白其区别。正好手里有项目用到static readonly,因此仔细研究了一翻。记录如下:



static readonly myclass1 c1 = new myclass1();

这里的初始化在定义时初始化,如果放在其它位置则报错。

image.png


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定定义,则报错:

image.png

原因很简单,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;


image.png

image.png


因此,勇哥总结一下:

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方法。

因此下面的任何企图修改列表值的意图都是失败的。


image.png


另个一种简单办法是,使用 .NET 4.5 中,List<T> 继承了 IReadOnlyList<T> 和 IReadOnlyCollection<T>,给了我们一种更简单的写法,同样可以达到上述效果。


勇哥最后说说为啥会写下此文。因为项目中有这样一个信号量:

image.png

其实不能写成划线处这样,而是要写样写:

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

版权声明:本文为博主原创文章,转载请附上博文链接!


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年5月    »
12345
6789101112
13141516171819
20212223242526
2728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864