我了解了 const
和 static readonly
字段。我们有一些仅包含常量值的类。它们在系统中用于各种目的。所以我想知道我的观察是否正确:
对于所有公共内容,这种类型的常量值是否应始终是 static readonly
?而仅对 internal
/ protected
/ private
值使用 const
?
您有什么建议?甚至我也不应该使用 static readonly
字段,而应该使用属性吗?
public static readonly
字段有些不同寻常;public static
属性(只有一个get
)会更为普遍(可能由private static readonly
字段支持)。
const
值被直接烧入调用站点;这有利有弊:
如果值永远不会更改,则使用const就可以了 - Zero
等比较合适作为常量;P除此之外,static
属性更为普遍。
readonly
字段不能在 switch/case
语句中使用,相反你需要将它们声明为 const
。 - Luciano如果消费者在不同的程序集中,我会使用static readonly
。 将const
和消费者放在两个不同的程序集中是一种让自己自寻烦恼的好方法。
internal const
或 public static readonly
。 - Iiridaynpublic const
有其有效用途(例如,任何标准的一部分。每当我使用XML时,都会有一个包含许多public const string
的命名空间文件)。但通常情况下,在充分考虑后才应使用public const
。 - Michael Stumconst int a
readonly int 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
有两个重载,一个接受short
(System.Int16
),另一个接受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
将比较System.Int16
和System.Int32
,由于运行时类型甚至不一致,这将产生false
。
我们得出结论,在某些(罕见)情况下,更改const
类型成员为static readonly
字段(或反过来,如果可能的话)可以更改程序的行为。
short x = 42;
合法化。因为在这里,你有一个 int
,即字面值 42
,它被隐式转换为 short x
。但随后,他们可能将其限制为仅限于数字字面值;然而,他们选择允许像 short x = y;
这样的东西,其中 y
被定义为 const int y = 42;
,于是就出现了这种情况。 - Jeppe Stig Nielsen需要注意的一点是,const 仅限于原始值类型(字符串是一个例外)。
System.Exception
中,"exception" 的意思是异常吗? :) - Memet Olsenconst
关键字:sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
、char
、float
、double
、decimal
、bool
以及任何 enum
类型。const
不能用于其他值类型,例如 DateTime
、TimeSpan
或 BigInteger
。它也不能用于 IntPtr
结构体(在某些情况下被认为是“原始”类型;在 C# 中,“原始”类型这个术语有些令人困惑)。 const
可以用于所有引用类型。如果类型是 string
,可以指定任何字符串值。否则,该值必须为 null
。 - Jeppe Stig Nielsen静态只读(Static Read Only):
该值可以在运行时通过 static
构造函数更改,但不能通过成员函数更改。
常量(Constant):
默认为 static
。该值无论是在构造函数、函数、运行时等任何地方都无法更改。
只读(Read Only):
该值可以在运行时通过构造函数更改,但不能通过成员函数更改。
您可以查看我的存储库:C# 属性类型。
readonly
关键字与const
关键字不同。一个const
字段只能在字段的声明时初始化。一个readonly
字段可以在声明或构造函数中初始化。因此,readonly
字段根据使用的构造函数可以具有不同的值。而且,虽然const
字段是编译时常量,但readonly
字段可用于运行时常量。
const
和readonly
很相似,但它们并不完全相同。
const
字段是编译时常量,这意味着该值可以在编译时计算。readonly
字段使得某些代码必须在类型构造期间运行的其他场景成为可能。构造完成后,readonly
字段不能被更改。
例如,const
成员可用于定义以下成员:
struct Test
{
public const double Pi = 3.14;
public const int Zero = 0;
}
因为像3.14和0这样的值是编译时常量。但是考虑到您定义一种类型并想要提供其某些预制实例的情况。例如,您可能希望定义一个颜色类并为常见颜色(如黑色、白色等)提供“常量”。使用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);
}
但是,如果没有任何方法防止颜色类的客户端对其进行操作,例如交换黑白值,这无疑会给其他颜色类客户端带来困扰。"只读"功能解决了这种情况。
仅通过在声明中引入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成员始终是静态的,而只读成员可以是静态的或非静态的,就像普通字段一样。
可以使用一个关键字来实现这两个目的,但这会导致版本问题或性能问题。暂时假设我们为此使用了一个关键字(const),并且开发人员编写了以下代码:
public class A
{
public static const C = 0;
}
另一位开发人员编写的代码依赖于 A:
public class B
{
static void Main() => Console.WriteLine(A.C);
}
现在,生成的代码能否依赖于 A.C 是一个编译时常量这一事实呢?也就是说,可以将对 A.C 的使用简单替换为值 0 吗?如果你回答“是”,那么就意味着 A 的开发者不能改变初始化 A.C 的方式——这样就未经许可限制了 A 的开发者。
如果你回答“否”,那么就会错过一个重要的优化机会。或许 A 的作者确信 A.C 总是为零。使用 const 和 readonly 都允许 A 的开发者指定意图,这会带来更好的版本控制行为和更好的性能。
我的偏好是尽可能使用const,正如先前的答案所提到的那样,这仅限于文字表达式或不需要求值的内容。
如果我遇到了这个限制,那么我会退而使用static readonly,但有一个例外。通常我会使用一个公共静态属性,它带有一个getter和一个支持的private static readonly字段,就像Marc在这里所提到的那样。
Const: 常量变量的值必须在声明时定义,之后就不会改变。const隐式地是静态的,因此我们可以在没有创建类实例的情况下访问它们。这个值在编译时确定。
ReadOnly: 我们可以在声明时以及在运行时使用构造函数定义只读变量的值。只读变量不能在没有类实例的情况下访问。
Static readonly: 我们可以在声明时以及仅通过静态构造函数而不是任何其他构造函数定义静态只读变量的值。我们还可以在没有创建类实例的情况下访问这些变量(作为静态变量)。如果我们必须在不同的程序集中使用变量,静态只读将是更好的选择。请在以下博客文章中查看完整细节:
static readonly
:尝试在IEnumerator
中使用const
会触发无法到达的yield
,导致编译器出现可怕的“内部编译器错误”。虽然我没有在 Unity3D 之外测试过这段代码,但我相信这是 mono 或 .NET 的 问题。然而,这仍然是一个关于 c# 的问题。 - cregoxswitch-case
语句中,不能使用static readonly
作为case
变量,需要使用const
来替代。翻译后的内容保持原意,通俗易懂,无解释,无额外内容返回。 - Mostafiz Rahmanstatic readonly
不能用作属性参数。 - Dread Boy