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

时间:2010-09-29 23:08:51

标签: 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添加'set'属性(而不是IFoo),但签名需要一个接口而不是具体对象。我不得不做一些演员。

// 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或制作只读函数参数而不添加其他属性或函数?

4 个答案:

答案 0 :(得分:8)

我认为您可能正在寻找一个涉及两个接口的解决方案,其中一个继承自另一个接口:

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只能在通过接口访问时抛出,而不能在通过类访问时抛出。

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

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

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

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

请勿将类更改为结构以满足此需求。这是个坏主意。但是如果它们应该是结构,现在你就会知道了。

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

答案 3 :(得分:0)

最接近的等价词是in关键字。 in设置参数input parameter,并禁止在方法内部进行更改。官方C#文档中的报价说:

  • in-指定此参数是通过引用传递的,但只能由被调用的方法读取。
  • ref-指定此参数通过引用传递,并且可以由调用的方法读取或写入。
  • out-指定此参数通过引用传递并由被调用方法写入。

enter image description here