.NET中的readonly/const在哪里?

30 .net language-design library-design

在C++中,你会看到void func(const T& t)无处不在.但是,我在.NET中没有看到类似的东西.为什么?

我注意到使用struct的参数很多.但是我看不到readonly/const的函数.事实上,现在我尝试了它,我不能使用这些关键字来制作一个承诺不修改传入的列表的函数.有没有办法让调用者承诺这个函数永远不会修改列表的内容?有没有办法说调用代码并说这个列表永远不应该被修改?(我知道我可以克隆列表或查看文档,但我喜欢编译错误)

LBu*_*kin 44

不变性仍然是C#成熟的领域.到目前为止,C#还没有采用constC++ 的语义......我认为这是一件好事.constC++中的行为经常使设计类层次结构变得具有挑战性,这些层次结构以您想要的方式工作.在不合需要的情况下,看到代码const_cast<>可以绕过constness,这种情况并不少见.希望C#的设计者能够发明一种更简单但仍然富有表现力的选择.

目前,没有语言功能将传递给方法的参数或对象标记为不可变.您可以做的最好的事情是使用仅允许读取操作的接口(或更好的包装器)传递对象.

对于.NET中的某些标准集合,您可以使用ReadOnlyCollection包装器,它ICollection在只读容器中封装任何可变类型.

创建不可变类型需要规划和了解语言功能.例如,readonly关键字是您的朋友.它允许您将类或结构的成员声明为不可变的.不幸的是,这种不变性仅适用于引用,而不适用于引用对象的成员.这意味着您可以声明:

private readonly int[] m_Values = new int[100];

public void SomeMethod()
{
    m_Values = new int[50]; // illegal, won't compile!
    m_Values[10] = 42;      // perfectly legal, yet undesirable
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,对数组的引用是不可变的,但数组的各个元素不是.当然,这种行为超出了数组.

我在设计不可变类型时发现有用的做法是将不可变行为分离到自己的接口,然后由管理数据的类实现.该接口仅公开get属性和方法,保证不会改变对象的状态.然后,可以将类型的实例作为该接口类型的参数传递给方法.这是一种弱不变性支持形式 - 因为被调用的方法通常可以将引用转换为可变类型.一个更好但更麻烦的替代方法是创建一个包装器实现,它实现相同的接口并维护对实际实例的引用(很像ReadOnlyCollection).这是更多的工作,但为不变性提供了更强有力的保障.

您选择使用的方法取决于保证不变性的重要性,以及您愿意花多少时间和精力来实现目标.

如果您有兴趣阅读有关此主题的更多信息,Eric Lippert 在C#中一系列关于不变性的优秀文章.

  • 我不同意这样的说法"看到用const_cast <>代码来绕过constness并不常见".用const_cast填充的C++代码表示代码设计器中的错误,而不是语言.在设计良好的代码中,const_cast极少使用,作为处理不接受const参数的遗留代码的最后手段.(对于用于缓存的数据成员,可以使用mutable关键字).无论如何,对于帖子的其余部分+1. (7认同)

Mar*_*ers 7

constC#中的方法参数没有修饰符.如果你想拥有类似于const保证的东西,你可以在接口中使用不可变类型.例如,方法可以接受IEnumerable<T>而不是可变集合类型.你也可以看看ReadOnlyCollection.


Joe*_*orn 5

我注意到使用struct的参数很多.

这里值得一提的是,与C++相比,C#夸大了结构和类之间的差异,夸大了值类型和引用类型之间的差异.在C#中,所有类都是引用类型,所有结构都是值类型.在.Net中,默认情况下,所有内容都按值传递.

值类型的这种区别的结果是在传递给函数时复制所有值类型.如果将结构传递给函数,则可以保证函数不会更改原始结构,因为该函数仅使用副本.将const添加到这样的参数是愚蠢的.

引用类型也按值传递.或者,更具体地说,引用本身是按值传递的.所以你有一个引用的副本,但它指向(指向)同一个对象.因此,函数退出后,函数对对象所做的更改将保持不变.

从表面上看,添加一个const选项,至少对于参考类型来说,似乎是一个好主意.它变得有点模糊,有副作用.或者,相反,这是如何实施的?显然,很容易说你的代码不能使用属性设置器.但其他方法呢?任何方法调用都可能需要更改对象.即使财产的getter可能有副作用,改变的东西(比如你实现一个安全功能时要注意的东西最后被访问).由于副作用,您是否会拒绝对某个属性的读取权限?

  • 如果可以将方法和属性getter声明为const,那么参数的const选项将起作用.如果对象作为const参数传递,则只调用const方法. (2认同)