C#能否强制转换const类型?

3
如果一个对象是只读或常量,是否有可能将该对象转换为可写?类似于C++中的const_cast。

2
你是在谈论对象本身还是对象的引用? - Tim Lloyd
3
如果你需要写入它,为什么不完全放弃const这个想法呢? - Lloyd
5个回答

13

在C#中不可能,就像在C++中一样。在C++中,如果对象确实是const的,你不能使用const_cast去掉const属性并且写入它,否则会引发未定义的行为:

struct foo { const int x; };

foo a;
int& b = const_cast<int&>(a.x);
b = 17; // invokes undefined behaviour

readonly 字段在 C# 中仅表示该字段本身无法重新分配。这类似于 C++ 中的 T *constT&。您可以通过其成员随意更改所引用的对象。

class Foo { public int x; }
class Bar { public readonly Foo y = new Foo(); }

Bar a = new Bar();
a.y.x = 3; // valid
a.y = new Foo(); // invalid

好的,我并没有说出全部真相。你可以通过反射1来作弊并更改readonly字段:

typeof(string).GetField("Empty").SetValue(null, "bar");
// this effectively makes string.Empty equal to "bar", with disastrous consequences
// It requires full trust though.
// Obviously, it's evil.

如果是一个const字段,即使使用这个技巧也无法工作。
const字段在使用它们的程序集中被硬编码,而不是保留对原始程序集的引用:
// Assembly A.dll
public class Foo { public static const int X = 42; }

// Assembly B.dll
int y = Foo.X;
// this is the equivalent to:
int y = 42;

这意味着,如果您重新编译 A.dll 并将 Foo.X 的值更改为 23,则 B.dll 将仍使用 42,直到重新编译 B.dll。
话虽如此,如果您想要更改字段,请不要将其设置为 readonly。如果您希望该字段可变但对外部是不可变的,请将其设置为私有,并添加只读属性(注意:这与 readonly 字段不同):
class Foo
{
    private int bar;
    public int Bar
    {
        get { return bar; }
    }
}

1并不是完全保证,但它在Microsoft实现上有效。如果您想知道为什么这个黑科技能够奏效,可以阅读Eric Lippert的解释。一定要读关于值类型上的readonly的答案。而且不用说了,不要在家里尝试


与C++不同的是,这并不是真的。只是C#引用的行为更像指针,而readonly更像Bar*const而不是const Bar* - Yakov Galka
6
在C++中,去掉const属性并不会导致未定义行为。例如,通过去掉const属性创建的非const引用或指针修改const对象才会导致未定义行为。只要您不改变const对象,就不会有未定义行为发生。因此,在您的示例中,去除const属性是完全可以的,尽管没有必要(您可以随时将const int赋值给int)。如果您尝试 const_cast<int>(a.x)=5; - 那就会有未定义行为发生。 - AndrzejJ
感谢大家的评论。我希望我已经修复了所有不太正确的细节 :) - R. Martinho Fernandes

1

你将无法修改 const 的值本身。你所能做的只是复制一份并更改该副本。除非我误解了问题...?

请记住,在 C# 中,const 是一个相当有限的关键字,你只能在编译时声明某些特定的东西为 const

http://msdn.microsoft.com/en-us/library/e6w8fe1b(v=VS.100).aspx

不确定为什么你想要改变一个常量。


1

不会。引用或值将只读。

但是,您可以修改引用的属性,或者只需复制一个值。


0

在CSharp中,您可以使用readonly进行转换(相当于C++中的const)。 CSharp中的const可以使用constexpr转换为C++,但无法强制转换。

要这样做,您可以调用Unsafe.AsRef - 它与C++中的const_cast具有相同的含义:

using System;

class App{
   static readonly int i = 42;
   static void Main(){
       Console.WriteLine(i);
       System.Runtime.CompilerServices.Unsafe.AsRef(i) = 777;
       Console.WriteLine(i);
   }
}

在C++中,它看起来是这样的:

#include <iostream>

class App{
public:
    static inline int iStorage= 42;
    static inline const int& i = iStorage;

    static void Main(){
       std::cout << i << '\n';
       *const_cast<int*>(&i) = 777;
       std::cout << i <<'\n';
   }
};

int main(){
    App::Main();
}

如果允许在static const的C++实现中将数据存储在只读内存中(这是标准明确允许的),那么就需要进行相关处理。 通常对于像int这样的简单类型执行此操作-原因是没有constexpr,但希望具有相同的行为-因此,static const有时与constexpr具有相同的含义。


0

对象不是只读或常量,只有变量才是。这意味着您只能将值分配给这样的变量一次(在const的情况下)或仅在所有者对象构造之前(在readonly的情况下)。

分配给readonly变量的对象仍然可以更改,除非它是不可变的(例如string)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接