C#的只读(“const”式)函数参数

来自C ++背景,我习惯于将const关键字粘贴到函数定义中,以使对象以只读值传递。 但是,我发现在C#中这是不可能的(如果我错了请纠正我)。 经过一些谷歌搜索后,我得出的结论是,制作只读对象的唯一方法是编写一个只有’get’属性并将其传递给的接口。 优雅,我必须说。

 public interface IFoo { IMyValInterface MyVal{ get; } } public class Foo : IFoo { private ConcreteMyVal _myVal; public IMyValInterface MyVal { get { return _myVal; } } } 

我会把它传递给:

 public void SomeFunction(IFoo fooVar) { // Cannot modify fooVar, Excellent!! } 

这可以。 但是,在我的其余代码中,我想正常修改我的对象。 向接口添加“set”属性会破坏我的只读限制。 我可以为Foo (而不是IFoo )添加一个’set’属性,但是签名需要一个接口而不是一个具体的对象。 我不得不做一些铸造。

 // Add this to class Foo. Might assign null if cast fails?? set { _myVal = value as ConcreteMyVal; } // Somewhere else in the code... IFoo myFoo = new Foo; (myFoo as Foo).MyFoo = new ConcreteMyVal(); 

是否有一种更优雅的方式来复制const或制作只读函数参数而无需添加其他属性或函数?

我想你可能正在寻找一个涉及两个接口的解决方案,其中一个inheritance自另一个接口:

 public interface IReadableFoo { IMyValInterface MyVal { get; } } public interface IWritableFoo : IReadableFoo { IMyValInterface MyVal { set; } } public class Foo : IWritableFoo { private ConcreteMyVal _myVal; public IMyValInterface MyVal { get { return _myVal; } set { _myVal = value as ConcreteMyVal; } } } 

然后你可以声明参数类型“告诉”它是否计划改变变量的方法:

 public void SomeFunction(IReadableFoo fooVar) { // Cannot modify fooVar, excellent! } public void SomeOtherFunction(IWritableFoo fooVar) { // Can modify fooVar, take care! } 

这模仿了类似于C ++中的constness的编译时检查。 正如Eric Lippert正确指出的那样,这与不变性不同。 但作为一名C ++程序员,我认为你知道这一点。

顺便说一句,如果您将类中的属性类型声明为ConcreteMyVal并分别实现接口属性,则可以实现稍微更好的编译时检查:

 public class Foo : IWritableFoo { private ConcreteMyVal _myVal; public ConcreteMyVal MyVal { get { return _myVal; } set { _myVal = value; } } public IMyValInterface IReadableFoo.MyVal { get { return MyVal; } } public IMyValInterface IWritableFoo.MyVal { // (or use “(ConcreteMyVal)value” if you want it to throw set { MyVal = value as ConcreteMyVal; } } } 

这样,setter只能在通过接口访问时抛出,而不能在通过类访问时抛出。

首先,你是对的:你不能将const或类似的关键字应用于C#中的参数。

但是,您可以使用接口沿这些行执行某些操作。 接口在某种意义上是特殊的,使得构建仅覆盖特征集的特定部分的接口非常有意义。 例如,映像一个堆栈类,它实现了IPopable和IPushable。 如果通过IPopable接口访问实例,则只能从堆栈中删除条目。 如果通过IPushable接口访问实例,则只能向堆栈添加条目。 您可以通过这种方式使用接口来获得与您要求的类似的东西。

首先考虑蒂姆维的回答。 但作为第二种选择,您可以这样做,使其更像C CONST关键字。

默认情况下,引用类型(对象)参数是IN参数。 但因为它们是引用,所以它们的方法副作用和属性访问是在方法之外的对象上完成的。 该对象不必被传递出去。 它仍然被该方法修改。

但是,值类型(struct)参数默认情况下也是IN,并且不能对传入的元素进行副作用或属性修改。而是在进入方法之前获得COPIED ON WRITE。 当方法超出范围(方法结束)时,该方法内的任何更改都会消失。

不要仅仅为了满足这种需要而将类更改为结构。 这是个坏主意。 但如果它们应该是结构,现在你会知道。

顺便说一句,一半的编程社区没有正确理解这个概念,但认为他们确实这样做了(事实上,我在几本书中发现了C#中参数方向问题的不准确性)。 如果您想评论我的陈述的准确性,请仔细检查以确保您知道您在说什么。