当前位置:首页 > C#编程 > C#/.net框架 > 正文内容

C# const, static readonly的测试

Jorge2年前 (2022-05-09)C#/.net框架667

 

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


static readonly myclass1 c1 = new myclass1();

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

image.png


const int myPI  跟常量是一样的,你可以必须使用常量的地方直接使用,比如下面的case中。这种常量变量是在编译时就确定了。

static readonly int  这个是在运行时计算出其值的,所以还能通过静态构造函数来赋值


有一种情况,无法使用const来定义常量,而又需要它是只读的量。

例如下面的情况:

C#
 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,才不想被其他类做任何修改!


这样的设计,感觉是不是有点奇怪呢?



C#
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>,如下:

C#
   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

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

扫描二维码推送至手机访问。

版权声明:本文由7点博客发布,如需转载请注明出处。

本文链接:http://6dot.cn/?id=71

标签: .NET.NET框架
分享给朋友:

“C# const, static readonly的测试” 的相关文章

C# 当前不会命中断点 还没有为该文档加载任何符号

C# 当前不会命中断点 还没有为该文档加载任何符号

这个问题网上的经验大概如下:1。 清空方案,重新编译2。 删除项目bin目录下的东西,重新编译3。 解决相互引用的问题。4。 确保不是run的release5。把项目编译改为x866。 好像没发现其它的了。。。这些解决不了我们手上的项目的问题。我们的工程有几十个项目。我长话短说,解决方法是:引导项目...

细说进程、应用程序域与上下文之间的关系(二)—— 应用程序域

细说进程、应用程序域与上下文之间的关系(二)—— 应用程序域

目录一、进程的概念与作用二、应用程序域三、深入了解.NET上下文四、进程应用程序域与线程的关系 二、应用程序域使用.NET建立的可执行程序 *.exe,并没有直接承载到进程当中,而是承载到应用程序域(AppDomain)当中。应用程序域是.NET引入的一个新概念,它比进程所占用的资源要少,...

细说进程、应用程序域与上下文之间的关系(一)——进程的概念与作用

细说进程、应用程序域与上下文之间的关系(一)——进程的概念与作用

引言本文主要是介绍进程(Process)、应用程序域(AppDomain)、.NET上下文(Context)的概念与操作。虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提高系统的性能有莫大的帮助。在本篇最后的一节当中将会介绍到三者与线程之间的关系,希望对多线程开发人员能提供...

如何在C#中调试LINQ查询

如何在C#中调试LINQ查询

在C#中我最喜欢的特性就是LINQ。使用LINQ, 我们可以获得一种易于编写和理解的简洁语法,而不是单调的foreach循环,它可以让你的代码更加美观。但是LINQ也有不好的地方,就是调试起来非常难。我们无法知道查询中到底发生了什么。我们可以看到输入值和输出值,但是仅此而已。当代码出现问题的时候,我...

C# 外观模式(Facade)

C# 外观模式(Facade)

1. 外观模式简介  外观模式主要解决的问题:当有多个类要处理时,需要一个个类去调用,没有复用性和扩展性。外观模式将处理子类的过程封装成操作,简化客户端的调用过程。1.1 定义  外观模式(Facade)通过提供一个统一接口,来访问子系统的多个接口。  使用外观模式时,创建一个统一的类,用来包装子系...

发表评论

访客

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