静态只读与const

时间:2009-04-16 11:21:43

标签: c# constants

我读过有关conststatic readonly字段的内容。我们有一些只包含常量值的类。用于我们系统中的各种事物。所以我想知道我的观察是否正确:

对于所有公开的内容,这些常量值是否总是static readonly?并且只对内部/受保护/私有值使用const

你推荐什么?我是否可能甚至不使用static readonly字段,而是使用属性?

19 个答案:

答案 0 :(得分:895)

public static readonly字段有点不寻常; public static属性(只有get)会更常见(可能由private static readonly字段支持)。

const值直接刻录到呼叫站点;这是双刃:

  • 如果在运行时获取值(可能来自config
  • ),则无效
  • 如果更改const的值,则需要重建所有客户端
  • 但它可以更快,因为它避免了方法调用......
  • ......无论如何有时候JIT都会内联

如果值从不更改,那么const就可以了 - Zero等进行合理的测试; p除此之外,static属性更常见。

答案 1 :(得分:224)

如果 Consumer 位于不同的程序集中,我会使用static readonly。将const消费者放在两个不同的程序集中是shoot yourself in the foot的好方法。

答案 2 :(得分:191)

需要注意的几个相关事项:

const int a

  • 必须初始化。
  • 初始化必须在编译时

readonly int a

  • 可以使用默认值,而无需初始化。
  • 初始化可以在运行时完成(编辑:仅在构造函数内)。

答案 3 :(得分:162)

这只是对其他答案的补充。我不会重复它们(现在四年后)。

在某些情况下,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有两个重载,一个接受shortSystem.Int16),另一个接受object({{1} })。现在的问题是一个或两个是否适用于我的System.Object参数。

y是编译时常量(文字),y情况时,确实存在来自 const的隐式转换 to int,前提是short是一个常量,前提是C#编译器验证其值是否在int范围内(short 1}}是。请参阅C#语言规范中的Implicit constant expression conversions。因此必须考虑两种重载。首选42是首选(任何Equals(short)都是short,但并非所有object都是object)。因此short转换为y,并使用该重载。然后short比较两个Equals相同的值,并给出short

true不是常量时,不存在从yint隐式转换。这是因为一般来说short可能太大而无法容纳int。 (显式转换确实存在,但我没有说short,因此不相关。)我们看到只有一个重载适用,{{1一个。因此,Equals((short)y)被设置为Equals(object)。然后y会将objectEquals进行比较,由于运行时类型甚至不同意,因此会产生System.Int16

我们得出结论,在某些(罕见)情况下,将System.Int32类型成员更改为false字段(或者在可能的情况下,将其更改)可以更改程序的行为。< / p>

答案 4 :(得分:87)

需要注意的一点是 const 仅限于原始/值类型(例外是字符串)

答案 5 :(得分:24)

readonly关键字与const关键字不同。 const字段只能在字段声明中初始化。可以在声明或构造函数中初始化readonly字段。因此,readonly字段可以具有不同的值,具体取决于所使用的构造函数。此外,虽然const字段是编译时常量,但readonly字段可用于运行时常量

Short and clear MSDN reference here

答案 6 :(得分:21)

静态只读: 可以在运行时通过static构造函数更改值。但不是通过成员函数。

常量: 默认情况下为static。价值不能从任何地方改变(Ctor,Function,runtime等no-where)。

只读: 可以在运行时通过构造函数更改值。但不是通过成员函数。

您可以查看我的回购:C# property types

答案 7 :(得分:15)

constreadonly相似,但它们并不完全相同。

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);
}

现在,生成的代码能否依赖于A.C是编译时常量的事实?即,A.C的使用可以简单地用值0代替吗?如果对此说“是”,那么这意味着A的开发人员无法改变A.C初始化的方式 - 这将未经许可将A的开发人员联系起来。

如果您对此问题说“不”,则会错过重要的优化。也许A的作者肯定A.C永远是零。 const和readonly的使用允许A的开发者指定意图。这样可以获得更好的版本控制行为以及更好的性能。

答案 8 :(得分:12)

我的偏好是尽可能使用 const ,如上所述,仅限于文字表达或不需要评估的内容。

如果我遇到这种限制,那么我会回到静态只读,但有一点需要注意。我通常会使用带有getter和后备 private static readonly 字段的公共静态属性,因为Marc提及here

答案 9 :(得分:7)

  

Const: Const只不过是“常量”,这是一个变量,其值在编译时是常量。并且必须为其分配值。默认情况下,const是静态的,我们不能在整个程序中更改const变量的值。

     

Static ReadOnly:静态只读类型变量的值可以在运行时分配,也可以在编译时分配,并在运行时更改。但是这个变量的值只能在静态构造函数中更改。而且无法进一步改变。它只能在运行时更改一次

参考:c-sharpcorner

答案 10 :(得分:6)

静态只读字段在暴露时是有利的 其他程序集中的值可能会在以后的版本中更改。

例如,假设程序集X公开一个常量,如下所示:

public const decimal ProgramVersion = 2.3;

如果程序集Y引用X并使用此常量,则值为2.3 编译时将被装入程序集Y。这意味着 如果稍后使用设置为2.4的常量重新编译XY仍然会 使用旧值2.3,直到重新编译Y。静止的 readonly字段避免了这个问题。

另一种看待这种情况的方法是任何可能的价值 根据定义,未来的变化不是一成不变的,因此也应如此 不能代表一个。

答案 11 :(得分:3)

常量:

  1. 值应在声明时给出
  2. 编译时间常数
  3. 只读:

    1. 值可以在声明时或在运行时使用构造函数给出。值可能因使用的构造函数而异。
    2. 运行时间常数

答案 12 :(得分:2)

C#.Net

中的const和静态只读字段之间存在细微差别

const必须在编译时用值初始化。

const默认是静态的,需要使用常量值进行初始化,以后不能修改。 它不能与所有数据类型一起使用。对于前日期时间。它不能与DateTime数据类型一起使用。

public const DateTime dt = DateTime.Today;  //throws compilation error
public const string Name = string.Empty;    //throws compilation error
public static readonly string Name = string.Empty; //No error, legal

readonly可以声明为static,但不是必需的。无需在声明时初始化。可以使用构造函数一次分配或更改其值。所以有可能一次改变readonly字段的值(无论如何,如果它是静态的),这对于const是不可能的。

答案 13 :(得分:1)

const :const变量值必须与声明一起定义,然后再声明             不会改变。 const是隐式静态的,因此无需创建类实例就可以访问它们。在编译时具有值

ReadOnly :我们可以在声明时定义的只读变量值,也可以在运行时使用构造函数。只读变量不能在没有类实例的情况下访问。

静态只读:我们可以在声明时定义静态静态变量值,并且只能通过静态构造函数来定义,而不能通过任何其他构造函数来定义。这些变量也可以在不创建类实例的情况下进行访问(作为静态变量)。

如果必须使用不同程序集中的变量,

静态只读将是更好的选择。请检查下面链接中的完整详细信息

https://www.stum.de/2009/01/14/const-strings-a-very-convenient-way-to-shoot-yourself-in-the-foot/

答案 14 :(得分:0)

常量就像名字所暗示的那样,字段不会改变,通常在编译时静态定义。

只读变量是在特定条件下可以更改的字段。

当您第一次将它们声明为常量时,它们可以初始化,但通常在构造函数内的对象构造期间初始化它们。

在初始化发生后,在上述条件下,它们无法更改。

静态只读对我来说是一个不好的选择,因为如果它是静态的并且永远不会改变,所以只要使用它就是公共const,如果它可以改变那么它不是一个常数然后,根据您的需要,您可以使用只读或只使用常规变量。

另外,另一个重要的区别是常量属于类,而只读变量属于实例!

答案 15 :(得分:0)

在switch语句或属性构造函数中不能使用只读静态变量的情况下,可以使用const(在编译时确定)。这是因为只读字段仅在运行时解析,并且某些代码构造需要编译时保证。可以在构造函数中计算只读静态变量,这通常是必不可少且有用的事情。差异是功能上的,我认为它们的用法也应如此。

就内存分配而言,至少对于字符串(作为引用类型)而言,似乎没有区别,因为两者都是被嵌入的,并且将引用一个被嵌入的实例。

就我个人而言,我的默认值是静态只读,因为它对我来说更具语义和逻辑意义,尤其是因为在编译时不需要大多数值。而且,顺便说一句,公开的只读静态数据在标记的回答状态中也并非罕见或罕见:例如,System.String.Empty是一个。

答案 16 :(得分:0)

声明 const 静态只读之间的另一个区别是内存分配。

静态字段属于对象的类型,而不是该类型的实例。结果,一旦第一次引用该类,静态字段将在其余时间“保留”在内存中,并且该类型的所有实例都将引用该静态字段的相同实例。

另一方面, const 字段“属于该类型的实例。

如果释放内存对您来说更重要,则最好使用 const 。如果速度快,则使用静态只读

答案 17 :(得分:0)

有一个重要的问题,上面的答案中没有提到,应该驱使您更喜欢“ const”,尤其是对于“ int”,“ string”等基本类型。

常量可以用作属性参数,不能使用静态只读字段!

Azure functions HttpTrigger, not using HttpMethods class in attribute

如果只有Microsoft将常量用于Http的GET,POST,DELETE等。

有可能写

[HttpTrigger(AuthorizationLeve.Anonymous,  HttpMethods.Get)] // COMPILE ERROR: static readonly, 

但是我不得不求助于

[HttpTrigger(AuthorizationLeve.Anonymous,  "GET")] // STRING

或者使用我自己的常量:

public class HttpConstants
{
    public const string Get = "GET";
}

[HttpTrigger(AuthorizationLeve.Anonymous,  HttpConstants.Get)] // Compile FINE!

答案 18 :(得分:0)

如果您可以提供编译时常量,请使用 const

private const int Total = 5;

如果您需要在运行时评估您的值,请使用 static readonly

private static readonly int GripKey = Animator.StringToHash("Grip");

这将导致编译错误,因为在编译时无法获取该值。

private const int GripKey = Animator.StringToHash("Grip");