27

我一直想知道为什么 C# 不支持const类或方法级别。我知道 Jon Skeet 长期以来一直希望支持不变性,并且我认为使用函数 const 的 C++ 语法可以帮助实现这一点。通过在类级别添加 const 关键字,我们将获得完全支持。

现在,我的问题是,C# 团队没有开发这种支持的原因是什么?

我想一切都可以通过编译时检查或通过属性来创建,而无需更改 CLR。我不介意代码能够通过反射覆盖 const 行为。

想象一下:

const class NumberContainer
{
    public int Number { get; }
}

.. 这样的类只能在构造时填充,所以我们需要一个构造函数来接收一个 int。

另一个例子是方法级别的 const :

public int AddNumbers(NumberContainer n1, NumberContainer n2) const
{
   return n1.Number + n2.Number;
}

常量级方法不应该能够改变它们自己的类或传递给它们的引用类型实例中的状态。此外,const 级函数只能在其范围内调用其他 const 级函数。

我不太确定 lambda 和委托是否会让一切变得太难(或不可能)实现,但我相信在语言和编译器设计方面有更多经验的人可以告诉我。

正如史蒂夫 B 在评论中指出的那样, 的存在readonly使事情变得更加复杂,因为 const 和readonlyduring 接近相同runtime,但是readonly在编译时无法确定值。我想我们可以拥有constreadonly水平,但这可能太混乱了?

那么,不执行此操作的原因是什么?可用性问题(新用户通常很难理解 C++ 中的常量)、语言设计问题(无法完成)或简单的优先级问题(不变性嗡嗡声的时代已经结束)......?

4

6 回答 6

26

冒着有点循环解释的风险,C# 不支持 const 因为 CLR 不支持它。CLR 不支持它,因为它完全不符合 CLS。

很少有语言有这个概念。C 语言支持 const,这在 C# 中通过readonly关键字得到了很好的支持。但最重要的当然是 C++,它对 const 有更广泛的适用性,毫无疑问是你要找的那个。我将避免确定 const 应该是什么意思,这本身就是一个虫洞,只谈论“const-ness”,即应用 const 的属性。

const-ness 的问题在于它需要被强制执行。当任意其他语言可以使用 C# 类并完全忽略 const-ness 只是因为该语言不支持它时,这是 C# 中的一个问题。仅仅因为 C# 支持它就将它固定到所有其他 CLS 语言上当然是非常不切实际的。

可执行性也是 C++ 中的一个问题。因为该语言还支持 const_cast<>。任何客户端代码都可以快速且无法诊断地消除 const-ness。你不应该这样做,但有时你必须这样做。因为有两种 const-ness,strict 和 observable。大致类似于私有常量和公共常量。mutable关键字后来被添加到语言中,以尝试处理对可观察 const 的需求,因此至少可以避免 const_cast<> 的不可避免的使用。有人说C++是一门难学的语言。很少听到 C# 的说法。

于 2012-04-11T08:36:23.567 回答
3

您说 CLR 不需要更改,但考虑到在编译的程序集中没有标准的方式来表达这种“常量”——而且这些程序集可能不会被 C# 代码使用。这不是你可以为 C# 做的事情 - 你必须为所有 .NET 语言做这件事。

于 2012-04-11T07:41:02.933 回答
2

正如我所相信的那样,const与 C++ 相比,C# 中的含义不同。

在 C# 中,您可以使用readonly关键字从const.

于 2012-04-11T07:41:05.663 回答
2

我曾经对以下情况感到惊讶:

class Vector
{
    private double[] m_data;
    public int Dimension {get;set;}

    public double this[int i]
    {
        get {return m_data[i];}
        set {m_data[i] = value;}
    }

    public Vector(int n)
    {
        this.Dimension = n;
        this.m_data = new double(n);
    }

    public static Vector Zero(int n)
    {
        Vector v = new Vector(n);
        for (int i = 0; i < n; i++)
        {
            v[i] = 0.0;
        }

        return v;
     }

    public static readonly Vector Zero3 = Zero(3);
}

你 Vector.Zero3 是只读的,你不能分配给它,你仍然可以访问它的组件,然后发生以下愚蠢的事情:

Vector a = Vector.Zero3;
a[0]     = 2.87;

现在,由于只是Vector.Vector3 的引用,因此后者也有 Vector.Vector3[0] == 2.87!

有一次掉进这个坑后,我发明了一个很简单的hack,虽然不优雅,但实现了它的功能。

即,在我想生成静态只读“常量”的类中,我引入了一个布尔标志:

class Vector
{
    private double[] m_data;
    public int Dimension {get;set;}
    private bool m_bIsConstant = false;
    ...

    public double this[int i]
    {
        get {return m_data[i];}
        set 
        {
           if (!m_bIsConstant)
           {
                m_data[i] = value;
           }
        }
    }
    ...
    public static Vector Zero(int n)
    {
        Vector v = new Vector(n);
        for (int i = 0; i < n; i++)
        {
            v[i] = 0.0;
        }

        v.m_bIsConstant = true;
        return v;
     }
     ...
}

这个 hack 保证你的静态只读变量永远不会被修改。

于 2012-11-29T14:56:23.593 回答
1

在您提出 const-class 的情况下,您说:

这样的类只能在构造时填充,所以我们需要一个构造函数来接收一个 int

但是无论如何,通过将所有属性设为只读,您已经实现了您所说的。

我不能代表 C# 语言设计者说话,但也许没有const应用于许多其他构造的原因是因为添加它根本不值得付出努力,您可以通过其他方式解决这个问题(如上所述和其他答案/评论)。

于 2012-04-11T07:58:38.780 回答
1

我无法从您的问题中看出,const关键字的这种重载将如何特别有益。

您的第一个示例可以合法地重写为

public class NumberContainer
{
    private readonly int number;

    public NumberContainer(int number)
    {
        this.number = number;
    }

    public int Number
    {
        get { return number; }
    }
}

也许,如果编译器无法辨别此类的不变性(我不知道),那么某些属性可能有用吗?

在你的第二个例子中,我不明白你在说什么。如果一个函数返回一个常量值,那么它可以被一个常量字段替换。

于 2012-04-11T08:00:09.343 回答