静态只读 vs 常量 — 不同程序集的视角?

13 浏览
0 Comments

静态只读 vs 常量 — 不同程序集的视角?

这个主题有很多问题,但除了一个短的问题之外,没有其他问题涉及以下情况。\n来自C#4书:\n[图片]\nMarc还写道:\n如果更改const的值,则需要重新构建所有客户端。\n问题:\n1)为什么会这样?static readonlyconst都是静态的吗?\n2)实际上值保存在哪里?\n3)将字段设置为static readonly实际上是如何在“幕后”解决这个问题的?

0
0 Comments

问题的原因是const在编译时解析,而static readonly是一个静态变量。static的值通常存储在堆上的一个特殊区域,称为高频堆。而const在编译时被替换。将其设置为static readonly将解决该问题,因为这样在运行时读取的是变量的值,而不是编译时提供的值。接下来,我们来详细讨论一下这个问题以及解决方法。

在.NET开发中,常常会使用const和static readonly来声明常量和只读变量。这两个关键字有着不同的作用和使用场景。

首先,我们来看一下const关键字。const是编译时常量,它的值在编译时就被确定,并且在运行时不能改变。例如:

const int MaxValue = 100;

在上面的代码中,MaxValue被定义为一个常量,它的值为100。在编译时,所有使用MaxValue的地方都会被替换为100。这种替换发生在编译时,所以const的值在运行时是不可变的。

接下来,我们来看一下static readonly关键字。static readonly是一个静态只读变量,它的值在运行时确定,并且在初始化之后就不能再改变。例如:

static readonly int MaxValue = 100;

在上面的代码中,MaxValue被定义为一个静态只读变量,它的值也是100。与const不同的是,static readonly的值是在运行时确定的,而不是在编译时。这意味着可以在运行时根据需要改变它的值。

那么,为什么会有static readonly vs const的区别呢?这是因为它们在不同的程序集中具有不同的行为。

首先,我们来看一下const在编译时被替换的特性。当一个const被定义在一个程序集中,并且在另一个程序集中被引用时,它的值会在编译时被替换为实际的值。这意味着,如果在引用程序集的代码中改变了const的值,那么在引用程序集重新编译之前,它的值不会被更新。这可能会导致潜在的问题和错误。

相比之下,static readonly的值是在运行时确定的,不会在编译时被替换。这意味着,当一个static readonly被定义在一个程序集中,并且在另一个程序集中被引用时,它的值是在运行时读取的,而不是在编译时提供的。这样就避免了const在不同程序集中的替换问题。

所以,如果在不同的程序集中引用一个常量或只读变量,并且希望在运行时读取其值而不是在编译时替换,那么应该使用static readonly而不是const。

static readonly和const是.NET开发中常用的关键字,用于声明常量和只读变量。它们有着不同的特性和使用场景。在不同的程序集中引用时,const会在编译时替换为实际的值,而static readonly会在运行时读取其值。因此,根据实际需求选择合适的关键字是很重要的。

0
0 Comments

问题的出现原因是对于const常量的理解错误,将其与static readonly字段混淆。解决方法是正确理解const常量的含义,并将其用于真正不会改变值的情况下。

静态只读字段和常量都是静态的,但它们在实现上有很大的区别。常量的值在编译时被嵌入到使用它的地方,因此它的值是不可变的。而静态只读字段的值在运行时被查找,可以在类的构造函数或字段初始化器中进行更改,但在其他地方是只读的。

在不同的程序集中,如果更改了常量的值,必须重新编译所有的客户端,因为常量的值已经在编译时被嵌入到客户端中。因此,在不同的程序集中使用常量可能会导致问题。

解决这个问题的方法是使用静态只读字段,而不是常量。静态只读字段的值在运行时查找,因此即使在其他程序集中更改了字段的值,客户端也能正确地获取到最新的值。

总结起来,静态只读字段和常量在实现上有很大的区别。常量的值在编译时被嵌入到使用它的地方,是不可变的;而静态只读字段的值在运行时被查找,可以在类的构造函数或字段初始化器中进行更改。在不同的程序集中,使用常量可能会导致问题,因此使用静态只读字段是一个解决方法。

0
0 Comments

Static readonly vs const — 不同程序集的观点?

在这里涉及的关键是以下内容的意义:

var foo = SomeType.StaticValue;

vs

var bar = SomeType.ConstValue;

在第一个情况下,它在运行时从`SomeType`中读取值,即通过`ldsfld`指令。然而,在第二种情况下,它被编译为该值本身。也就是说,如果`ConstValue`恰好是`123`,那么第二种情况就等同于:

var bar = 123;

在运行时,它来自`SomeType`的事实并不存在,因为该值(`123`)是由编译器评估并存储的。因此,它需要重新构建才能获取新的值。

将其更改为`static readonly`意味着“从`SomeType`加载该值”的操作被保留。

因此,以下代码:

static int Foo()
{
    return Test.Foo;
}
static int Bar()
{
    return Test.Bar;
}
...
static class Test
{
    public static readonly int Foo = 123;
    public const int Bar = 456;
}

编译为:

.method private hidebysig static int32 Bar() cil managed
{
    .maxstack 8
    L_0000: ldc.i4 0x1c8
    L_0005: ret 
}
.method private hidebysig static int32 Foo() cil managed
{
    .maxstack 8
    L_0000: ldsfld int32 ConsoleApplication2.Test::Foo
    L_0005: ret 
}

请注意,在`Bar`中,`ldc`指令直接加载一个值(0x1c8 == 456),完全没有`Test`的存在。

为了完整起见,`const`是使用静态字段实现的,但是 - 它是一个字面字段,意味着:在编译时计算,而不是运行时计算。

.field public static literal int32 Bar = int32(0x1c8)
.field public static initonly int32 Foo

感谢出色的回答,但是`const`隐式地是`static`吗?

嗯,是的,就IL代码而言,它是作为`.field ... static`实现的,并且在C#中通过`TypeName.ConstName`访问 - 但是重要的是语义;在访问常量成员和访问常规静态成员(字段/属性)之间存在巨大的区别。

0