静态只读 vs 常量

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

这些常量值是否应该对所有公共内容始终保持static readonly ?并且仅将const用于内部 / 受保护 / 私有值吗?

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

答案

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

const值直接刻录到调用站点中;这是双刃的:

  • 如果在运行时获取值(可能是从 config 中获取)是没有用的
  • 如果更改 const 的值,则需要重建所有客户端
  • 但它可以更快,因为它避免了方法调用...
  • ... JIT 有时可能内联了

如果该值永远不变,则 const 很好 - Zero等可以使 const 变得合理; p 除此之外, static属性更为常见。

如果使用者在不同的程序集中,我将使用static readonly 。将constConsumer放在两个不同的程序集中是一个不错的选择

很少有其他需要注意的事情:

const int a

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

只读 int

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

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

在某些情况下, 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 ),一个重载为objectSystem.Object )。现在的问题是我的y参数是否适用于其中之一。

y是编译时常量(文字)(即const情况)时,重要的是,确实存在 int short的隐式转换前提是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 。不能在任何地方(Ctor,Function,运行时等,无处)更改值。

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

您可以看一下我的仓库: 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 ,如上所述,它仅限于文字表达式或不需要求值的内容。

如果我遇到了这个限制,那么我会退一步回到静态 readonly ,但要注意一点。正如 Marc 在这里提到的,我通常会使用带有 getter 和后备私有 static readonly字段的公共静态属性。

常量:常量就是 “常量”,它的值是恒定的,但在编译时才是常量。并且必须为其分配值。默认情况下,const 是静态的,我们无法在整个程序中更改 const 变量的值。

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

参考: c-sharpcorner