繁体   English   中英

“静态只读”与“常量”

[英]'Static readonly' vs. 'const'

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

对于所有公开的内容,这些常量值是否应该始终为static readonly 并且仅将const用于internal / protected / private值?

你有什么建议吗? 我是否应该甚至不使用static readonly字段,而是使用属性?

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

const值直接烧入调用站点; 这是双刃剑:

  • 如果该值是在运行时获取的,可能是从配置中获取的,则它是无用的
  • 如果你改变了一个 const 的值,你需要重建所有的客户端
  • 但它可以更快,因为它避免了方法调用......
  • ...无论如何有时可能已被 JIT 内联

如果值永远不会改变,那么 const 就可以了 - Zero等构成合理的常量 ;p 除此之外, static属性更常见。

如果消费者在不同的程序集中,我将使用static readonly constConsumer放在两个不同的程序集中是一种很好的打击自己的方式。

还有一些需要注意的相关事项:

const int a

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

只读 int a

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

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

在某些情况下, 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的情况下,它有不存在的隐式转换变得重要intshort规定, int是一个常数,并且条件是C#编译器验证它的值是内short的范围( 42是)。 请参阅 C# 语言规范中的隐式常量表达式转换 所以这两种重载都必须考虑。 重载Equals(short)是首选(任何short都是object ,但并非所有object都是short )。 所以y被转换为short ,并且使用了那个重载。 然后Equals比较两个short的相同值,并给出true

y不是常数时,不存在从intshort隐式转换。 那是因为通常int可能太大而无法放入short (确实存在显式转换,但我没有说Equals((short)y) ,所以这无关紧要。)我们看到只有一个重载适用,即Equals(object)一个。 所以y被装箱到object 然后Equals会将System.Int16System.Int32进行比较,并且由于运行时类型甚至不一致,这将产生false

我们得出结论,在某些(罕见)情况下,将const类型成员更改为static readonly字段(或其他方式,如果可能)可以更改程序的行为。

需要注意的一件事是const仅限于原始/值类型(字符串除外)。

静态只读

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

常数

默认情况下是static 不能从任何地方(构造函数、函数、运行时等)更改值。

只读

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

您可以查看我的存储库: C# 属性类型

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

简短明了的 MSDN 参考在这里

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

const字段是编译时常量,这意味着可以在编译时计算该值。 readonly字段支持在类型的构造期间必须运行某些代码的其他场景。 构建后, readonly字段不能更改。

例如, const成员可用于定义如下成员:

struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

因为像 3.14 和 0 这样的值是编译时常量。 但是,请考虑您定义类型并希望提供它的一些预制实例的情况。 例如,您可能想要定义一个 Color 类并为常见颜色(如黑色、白色等)提供“常量”。使用 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 的客户端使用它,也许是通过交换 Black 和 White 值。 不用说,这会引起 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 ,正如前面的答案中提到的,它仅限于文字表达式或不需要评估的东西。

如果我遇到了这个限制,那么我会退回到static readonly ,但有一个警告。 我通常会使用带有 getter 的公共静态属性和支持私有静态只读字段,正如 Marc 在此处提到的那样。

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

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

参考: c-sharpcorner

当向其他程序集公开可能在更高版本中更改的值时,静态只读字段是有利的。

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

public const decimal ProgramVersion = 2.3;

如果程序集Y引用X并使用此常量,则值 2.3 将在编译时烘焙到程序集Y 这意味着如果X稍后在常量设置为 2.4 的情况下重新编译,则Y仍将使用旧值 2.3,直到重新编译Y 静态只读字段避免了这个问题。

另一种看待这个问题的方式是,任何可能在未来发生变化的值在定义上都不是恒定的,因此不应表示为一个。

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

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

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

如果我们必须使用不同程序集中的变量,静态只读将是更好的选择。 请查看以下博客文章中的完整详细信息:

Const Strings – 一种非常方便的用脚射击自己的方法

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 可以声明为静态,但不是必需的。 无需在声明时初始化。 可以使用构造函数一次分配或更改其值。 所以有可能改变 readonly 字段的值一次(无关紧要,如果它是静态的),这是 const 不可能的。

常量:

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

只读:

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

const(在编译时确定)可用于只读静态不能的情况,例如在 switch 语句或属性构造函数中。 这是因为只读字段仅在运行时解析,并且某些代码构造需要编译时保证。 可以在构造函数中计算只读静态,这通常是必不可少且有用的东西。 区别是功能性的,在我看来它们的用法应该如此。

在内存分配方面,至少对于字符串(作为引用类型),两者似乎没有区别,因为两者都被实习并且将引用一个实习实例。

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

声明conststatic readonly 的另一个区别在于内存分配。

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

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

如果释放内存对您来说更重要,则更喜欢使用const 如果速度,则使用static readonly

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

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

它们可以在您第一次像常量一样声明它们时被初始化,但通常它们是在构造函数内部的对象构造过程中初始化的。

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

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

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

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

常量可以作为属性参数,静态只读字段不行!

Azure 函数 HttpTrigger,未在属性中使用 HttpMethods 类

如果只有微软使用 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!

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

private const int Total = 5;

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

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

这将产生编译错误,因为在编译时无法获得该值。

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

常量

  1. 只能应用于字段。 值应该在代码编译时。
  2. 适合在编译代码之前在代码中删除魔术“字符串”、“int/double”、(原始类型)等。
  3. 编译后,该值将放置在使用常量的所有编译代码中。 所以如果你有一个巨大的字符串在很多地方使用,那么在使它成为 const 之前要小心。 考虑使用静态只读。

静态只读

  1. 静态只读适用于字段/道具,静态可用于方法。 (附注)当静态应用于方法时,编译后的代码不会将“this”参数传递给方法,因此您无法访问对象的实例数据。
  2. 适用于编译代码后可能更改的值。 像从配置中初始化的值,在应用程序启动期间等。
  3. 编译代码后,在 IL 代码中使用 ref to value 可能比使用 const 慢,但编译后的代码很小

在重构期间,所有 const 都可以安全地转换为静态只读,但反之亦然,正如我们在上面看到的,当转换的代码可能会中断,因为可以在构造函数中初始化一些静态只读变量。

上面提到的另一个我不相信的区别是:

conststatic readonly值不会在 Visual Studio IDE中应用 CodeLens。

static仅获取属性 DO 将 CodeLens 应用于它们。

代码镜头示例

我认为添加 CodeLens 非常有价值。

注意:当前使用 Visual Studio 2022。

Const, readonly, static readonly - 执行类似操作但有重要区别的关键字:

Const -是一个变量,其值为常量并在编译时分配。 您必须为其分配一个值。 默认常量是static,我们不能在整个程序中改变const变量的值。

Readonly -表示我们可以在运行时更改的值,或者我们可以在运行时分配它,但只能通过非静态构造函数。

Static 只读-值可以在运行时分配或在编译时分配并在运行时更改。 但是这个变量的值只能在 static 构造函数中改变。 并且无法进一步更改。 它只能在执行期间更改一次。

您可以在这里找到示例 - https://www.c-sharpcorner.com/UploadFile/c210df/difference-between-const-readonly-and-static-readonly-in-C-Sharp/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM