在 C++ 中,您将void func(const T& t)
随处可见。但是,我在 .NET 中没有看到任何类似的东西。为什么?
我注意到大量使用 struct 的参数。但我看不到带有只读/常量的函数。事实上,现在我试过了,我无法使用这些关键字来创建一个承诺不修改传入列表的函数。有没有办法向调用者承诺这个函数永远不会修改列表的内容?有没有办法说调用代码并说这个列表永远不应该被修改?(我知道我可以克隆列表或查看文档,但有时我喜欢编译错误)
在 C++ 中,您将void func(const T& t)
随处可见。但是,我在 .NET 中没有看到任何类似的东西。为什么?
我注意到大量使用 struct 的参数。但我看不到带有只读/常量的函数。事实上,现在我试过了,我无法使用这些关键字来创建一个承诺不修改传入列表的函数。有没有办法向调用者承诺这个函数永远不会修改列表的内容?有没有办法说调用代码并说这个列表永远不应该被修改?(我知道我可以克隆列表或查看文档,但有时我喜欢编译错误)
不变性仍然是 C# 成熟的领域。到目前为止,C# 还没有采用const
C++ 的语义......我实际上认为这是一件好事。C++ 中的行为const
通常使设计以您想要的方式工作的类层次结构具有挑战性。在不希望出现的情况下,看到代码中包含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
}
在上面的示例中,对数组的引用是不可变的,但数组的各个元素不是。当然,这种行为超出了数组。
在设计不可变类型时,我发现一种有用的做法是将不可变行为分离到自己的接口中,然后由管理数据的类实现。该接口仅公开保证不会改变对象状态的 get 属性和方法。然后可以将您的类型的实例作为该接口类型的参数传递给方法。这是不可变支持的弱形式 - 因为被调用的方法通常可以将引用强制转换为可变类型。一个更好但更麻烦的替代方案是创建一个实现相同接口并维护对实际实例的引用的包装器实现(很像ReadOnlyCollection
)。这是更多的工作,但为不变性提供了更强的保证。
您选择使用的方法取决于不变性保证的重要性,以及您愿意花费多少时间和精力来实现这一目标。
如果您有兴趣阅读有关此主题的更多信息,Eric Lippert 有一系列关于 C# 中不变性的优秀文章。
C# 中方法参数没有const
修饰符。如果您想拥有类似于 const 的保证,您可以在接口中使用不可变类型。例如,一个方法可以接受一个IEnumerable<T>
而不是一个可变集合类型。你也可以看看ReadOnlyCollection
。
我注意到大量使用 struct 的参数。
这里值得一提的是,与 C++ 相比,C# 通过夸大值类型和引用类型之间的差异,夸大了结构和类之间的差异。在 C# 中,所有类都是引用类型,所有结构都是值类型。在 .Net 中,默认情况下,所有内容都是按值传递的。
值类型的这种区别的结果是,所有值类型在传递给函数时都会被复制。如果将结构传递给函数,则可以保证该函数不会更改原始结构,因为该函数仅使用副本。将 const 添加到这样的参数是愚蠢的。
引用类型也是按值传递的。或者,更具体地说,引用本身是按值传递的。所以你有一个引用的副本,但它指向(引用)同一个对象。因此,函数对对象所做的更改将在函数退出后持续存在。
从表面上看,添加一个const
选项,至少对于引用类型,似乎是个好主意。它变得有点模糊的地方是副作用。或者,更确切地说,这是如何执行的?显然,很容易说您的代码不能使用属性设置器。但是其他方法呢?任何方法调用都可能需要更改对象。即使是属性获取器也可能具有改变某些内容的副作用(例如,您实现了一个安全功能来记录上次访问某些内容的时间)。您是否会因为副作用而拒绝对属性的读取访问?
问题是 C/C++ 中使用的 const 正确性仅由编译器强制执行。顺便说一句,很容易绕过。CLR 实际上在托管代码中强制执行类型安全,而不是在编译器中。之所以如此,是因为 .NET 框架支持多种语言。类型系统和交互规则在 CLI 中进行了布局,这是一种需要服务于许多目标和大师的通用语言基础设施。
const 正确性在当前语言中很少见。就个人而言,我只知道 C++,一种未移植到 CLI 的语言。对语法相去甚远的语言强制执行规则是困难的。像没有在 CLS 中的无符号整数类型这样简单的东西在基础组件的设计上留下了显着的印记。
不变性(真实的,不是用编译器伪造的)让 MSFT 团队不断思考。它很受欢迎,我知道 C# 团队一直在考虑它。适合并发等。Eric Lippert 做了一个11 部分的博客文章系列,不过有点逐渐消失。对于一个人和他的观众来说太大了。如果有真正的行动来实现它,我相信他们会仔细考虑并使其发挥作用。某天。某种语言。
据我所知,这是不可能的。也许这篇文章能给你一些想法: