我已经阅读了各地const
和static readonly
各个领域.我们有一些只包含常量值的类.用于我们系统中的各种事物.所以我想知道我的观察结果是否正确:
这些常量值是否应始终static readonly
适用于所有公开的内容?并且仅const
用于内部/受保护/私有值?
您有什么推荐的吗?我是否可能甚至不使用static readonly
字段,而是使用属性?
public static readonly
田地有点不寻常; public static
属性(只有a get
)会更常见(可能由private static readonly
字段支持).
const
值直接烧入呼叫站点; 这是双刃:
如果在运行时获取值,可能是从config获取它是没用的
如果更改const的值,则需要重建所有客户端
但它可以更快,因为它避免了方法调用...
......无论如何,JIT有时可能已将其内联
如果值永远不会改变,那么const就可以了 - Zero
等等做出合理的反应; p除此之外,static
属性更常见.
static readonly
如果Consumer在不同的程序集中,我会使用.拥有const
和消费者在两个不同的组件是一个很好的方式射击自己的脚.
const int a
必须初始化.
初始化必须在编译时.
readonly int a
可以使用默认值,而无需初始化.
初始化可以在运行时完成(编辑:仅在构造函数内).
这只是对其他答案的补充.我不会重复它们(现在四年后).
在某些情况下,a const
和非const具有不同的语义.例如:
const int y = 42; static void Main() { short x = 42; Console.WriteLine(x.Equals(y)); }
打印出来True
,而:
static readonly int y = 42; static void Main() { short x = 42; Console.WriteLine(x.Equals(y)); }
写道False
.
原因是该方法x.Equals
有两个重载,一个接受a short
(System.Int16
),另一个接受a object
(System.Object
).现在问题是一个或两个是否适用于我的y
论点.
当y
是一个编译时间常数(字面),所述const
情况下,也有不存在的隐式转换变得重要从 int
到 short
提供的int
是一个常数,并且条件是C#编译器验证其值的范围内short
(这42
是).请参阅C#语言规范中的隐式常量表达式转换.所以必须考虑两个重载.过载Equals(short)
是优选的(任何short
是一个object
,但不是全部object
是short
).因此y
转换为short
,并使用该重载.然后Equals
比较两个short
相同的值,并给出true
.
当y
不是一个常数,不隐含从转换int
到short
存在.那是因为一般情况下int
可能太大而无法适应short
.(显式转换确实存在,但我没有说Equals((short)y)
,所以这不相关.)我们看到只有一个重载适用,Equals(object)
一个.所以y
是盒装到object
.然后Equals
将比较a System.Int16
和a System.Int32
,并且因为运行时类型甚至不同意,所以会产生false
.
我们得出结论,在某些(罕见)情况下,将const
类型成员更改为static readonly
字段(或者在可能的情况下,将其更改为方式)可以更改程序的行为.
需要注意的一点是const仅限于原始/值类型(异常是字符串)
静态只读:可以static
在运行时通过构造函数更改值.但不是通过成员函数.
常量:默认情况下static
.无法从任何地方更改值(Ctor,Function,runtime等no-where).
只读:可以在运行时通过构造函数更改值.但不是通过成员函数.
你可以看一下我的repo:C#属性类型.
所述readonly
关键字是从不同的const
关键字.一个const
字段只能在外地的声明进行初始化.一个readonly
字段可以在声明或构造函数初始化.因此,readonly
根据使用的构造函数,字段可以具有不同的值.此外,虽然const
字段是编译时常量,但该readonly
字段可用于运行时常量
这里简短而明确的MSDN参考
const
并且readonly
是相似的,但它们并不完全相同.
甲const
字段是一个编译时间常数,这意味着该值可以在编译时计算.甲readonly
字段允许在其中一些代码必须结构的类型的期间运行附加情景.施工后,readonly
不能改变油田.
例如,const
成员可用于定义以下成员:
struct Test { public const double Pi = 3.14; public const int Zero = 0; }
因为像3.14和0这样的值是编译时常量.但是,请考虑您定义类型并希望提供一些预制实例的情况.例如,您可能希望定义一个Color类并为常用颜色(如Black,White等)提供"常量".使用const成员不可能这样做,因为右侧不是编译时常量.可以使用常规静态成员执行此操作:
public class Color { public static Color Black = new Color(0, 0, 0); public static Color White = new Color(255, 255, 255); public static Color Red = new Color(255, 0, 0); public static Color Green = new Color(0, 255, 0); public static Color Blue = new Color(0, 0, 255); private byte red, green, blue; public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b); }
但是,通过交换黑白值,没有什么能阻止Color的客户端使用它.不用说,这会导致Color类的其他客户端惊慌失措."只读"功能解决了这种情况.
通过readonly
在声明中简单地引入关键字,我们保留了灵活的初始化,同时防止客户端代码混乱.
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, green, blue) = (r, g, b); }
有趣的是,const成员总是静态的,而readonly成员可以是静态的,也可以不是静态的,就像常规字段一样.
可以将单个关键字用于这两个目的,但这会导致版本控制问题或性能问题.假设我们为此(const)使用了一个关键字,开发人员写道:
public class A { public static const C = 0; }
而另一个开发人员编写的代码依赖于A:
public class B { static void Main() => Console.WriteLine(A.C); }
现在,生成的代码能否依赖于AC是编译时常量的事实?即,AC的使用是否可以简单地用值0代替?如果你对此说"是",那么这意味着A的开发人员无法改变AC初始化的方式 - 这在未经许可的情况下将A的开发人员联系在一起.
如果你对这个问题说"不",那么就会错过重要的优化.也许A的作者肯定AC会永远为零.const和readonly的使用允许A的开发者指定意图.这样可以获得更好的版本控制行为以及更好的性能.
我倾向于尽可能使用const,如上所述,它仅限于文字表达式或不需要评估的东西.
如果我对这个限制感到热烈,那么我只需要一个警告即可回退到静态读取状态.我通常会使用带有getter和后备私有静态只读字段的公共静态属性,如Marc 在这里提到的那样.
Const: Const只不过是"常量",这是一个变量,其值在编译时是常量.并且必须为其分配值.默认情况下,const是静态的,我们不能在整个程序中更改const变量的值.
Static ReadOnly:静态Readonly类型变量的值可以在运行时分配,也可以在编译时分配,并在运行时更改.但是这个变量的值只能在静态构造函数中更改.而且无法进一步改变.它只能在运行时更改一次
参考:c-sharpcorner
当向其他程序集公开可能在更高版本中更改的值时,静态只读字段是有利的.
例如,假设程序集X
公开一个常量,如下所示:
public const decimal ProgramVersion = 2.3;
如果程序集Y
引用X
并使用此常量,则值2.3将Y
在编译时被烘焙到程序集中.这意味着如果X
稍后使用常量设置为2.4重新编译,Y
仍将使用旧的值2.3直到Y
重新编译.静态只读字段可以避免此问题.
另一种看待这种情况的方法是,根据定义,未来可能发生变化的任何值都不是恒定的,因此不应该表示为一个.