在C ++中,你会看到void func(const T& t)无处不在。 但是,我在.NET中没有看到类似的东西。 为什么?
我注意到使用struct的参数很多。 但是我看不到readonly / const的函数。 事实上,现在我尝试了它,我不能使用这些关键字来制作一个承诺不修改传入的列表的函数。有没有办法让调用者承诺这个函数永远不会修改列表的内容? 有没有办法说调用代码并说这个列表永远不应该被修改? (我知道我可以克隆列表或查看文档,但我喜欢编译错误)
不变性仍然是C#成熟的领域。到目前为止,C#还没有采用C ++的const语义......我认为这是一件好事。 C ++中const的行为经常使设计类层次结构变得具有挑战性,这些层次结构按照您想要的方式工作。看到代码充满了const_cast<>以绕过不可取的常量并不罕见。希望C#的设计者能够发明一种更简单但仍然富有表现力的选择。
目前,没有语言功能将传递给方法的参数或对象标记为不可变。您可以做的最好的事情是使用仅允许读取操作的接口(或更好的包装器)传递对象。
对于.NET中的某些标准集合,您可以使用ReadOnlyCollection包装器,它在只读容器中封装任何可变ICollection类型。
创建不可变类型需要规划和了解语言功能。例如,readonly关键字是您的朋友。它允许您将类或结构的成员声明为不可变的。不幸的是,这种不变性仅适用于引用,而不适用于引用对象的成员。这意味着您可以声明:
1 2 3 4 5 6 7
| 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#中有一系列关于不变性的优秀文章。
-
我不同意这样的说法"看到用const_cast <>代码来绕过constness并不常见"。用const_cast填充的C ++代码表示代码设计器中的错误,而不是语言。在设计良好的代码中,const_cast极少使用,作为处理不接受const参数的遗留代码的最后手段。 (对于用于缓存的数据成员,可以使用mutable关键字)。无论如何,对于帖子的其余部分+1。
-
@Andrew Shepherd:我同意你的观点,设计良好的代码通常不需要const_cast,但实际上编码或设计const是许多初级开发人员很难掌握的东西。这不是你在CS / CE课程中经常教授的东西;当推动推进时(翻译:时间不多了,以完成你的工作),开发人员将依赖任何使他们的代码工作的方法。这很不幸,但并不罕见。我更大的一点是,如果C#团队能够提出一个更简单的方案,以便更多的开发人员可以接受它,那就太好了。
-
@AndrewShepherd:如果我错了,请纠正我,但我认为在代码可能需要接收和存储(参与者,目标)对的情况下,最终需要const-casting,其供应商保证修改其任何actor target将仅与非const target实例配对。如果没有演员修改他们的目标,或者没有目标是const,那么就没有必要进行const-casting,但是我认为没有办法创建一个概念,即修改目标的actor只会与non配对 - 对象。现在有办法做到这一点吗?
C#中的方法参数没有const修饰符。如果你想拥有类似于const保证的东西,你可以在接口中使用不可变类型。例如,方法可以接受IEnumerable< T >而不是可变集合类型。你也可以看看ReadOnlyCollection。
-
值得注意的是(如果问题op不知道)您可以将列表直接传递给期望IEnumerable的方法
-
你们俩可以告诉我IEnumerable和ReadOnlyCollection之间有什么区别吗?我有一种感觉,唯一的区别是IEnumerable可以对一个容器进行类型转换然后编辑(或者可能通过反射?也许不是?)而ReadOnlyCollection不能?
-
@ acidzombie24:IEnumerable是集合类实现的接口,允许它们参与可以枚举(迭代)内容的类型的生态系统。 ReadOnlyCollection是一个特殊的包装类,它组合(聚合)一个ICollection实例并仅公开只读操作。遗憾的是,ROC仍然提供Add和Remove之类的方法,但在调用它们时抛出运行时异常 - 使编译时的不可变执行力弱于可能的。顺便提一下,大部分LINQ都是基于IEnumerable< T >接口构建的。
I have notice a nice amount of parameters using struct.
这里值得一提的是,与C ++相比,C#夸大了结构和类之间的区别,夸大了值类型和引用类型之间的差异。在C#中,所有类都是引用类型,所有结构都是值类型。在.Net中,默认情况下,所有内容都按值传递。
值类型的这种区别的结果是在传递给函数时复制所有值类型。如果将结构传递给函数,则可以保证函数不会更改原始结构,因为该函数仅使用副本。将const添加到这样的参数是愚蠢的。
引用类型也按值传递。或者,更具体地说,引用本身是按值传递的。所以你有一个引用的副本,但它指向(指向)同一个对象。因此,函数退出后,函数对对象所做的更改将保持不变。
从表面上看,添加const选项(至少对于参考类型)似乎是个好主意。它变得有点模糊,有副作用。或者,相反,这是如何实施的?显然,很容易说你的代码不能使用属性设置器。但其他方法呢?任何方法调用都可能需要更改对象。即使是属性获取者也可能会产生一些改变某些东西的副作用(比如说你实现了一个安全功能来记录上次访问的东西)。由于副作用,您是否会拒绝对某个属性的读取权限?
-
好吧,C ++有一个可变关键字stackoverflow.com/questions/105014/c-mutable-keyword。我同意,如果添加const会变得复杂,但我个人不需要使用mutable。所以复杂性应该很少见。
-
如果可以将方法和属性getter声明为const,那么参数的const选项将起作用。如果对象作为const参数传递,则只调用const方法。
问题是C / C ++中使用的const正确性仅由编译器强制执行。顺便绕过btw。 CLR实际上在托管代码中强制执行类型安全,而不是编译器。这是必要的,因为.NET框架支持多种语言。类型系统和交互规则在CLI中进行布局,CLI是一种需要服务于许多目标和主人的通用语言基础结构。
在当前语言中,Const正确性很少见。就个人而言,我只知道C ++,一种未移植到CLI的语言。对语言无法接近的语言实施规则很困难。像CLS中没有的无符号整数类型那样简单的东西在基础组件的设计上留下了显着的标记。
不可变性(真实的,不是伪造的编译器)使MSFT团队保持思考。它很受欢迎,我知道C#团队一直在考虑它。适用于并发,等等。埃里克·利珀特(Eric Lippert)做了一篇关于它的11篇博文系列,但有点消失了。一个人和他的观众太大了。如果真的要实现它,我相信它们会通过思考并使其发挥作用。有一天。一些语言。
据我所知,这是不可能的。也许这篇文章给你一些想法:
http://www.c-sharpcorner.com/UploadFile/bulentozkir/PassingConstInCS11082005014622AM/PassingConstInCS.aspx
-
这个也很有用:blogs.msdn.com/abhinaba/archive/2006/01/18/513906.aspx