来自 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!!
}
这很好。但是,在我的其余代码中,我想正常修改我的对象。向界面添加“设置”属性会打破我的只读限制。我可以向
Foo
(而不是 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
或制作只读函数参数而不添加其他属性或函数?
我认为您可能正在寻找一种涉及两个接口的解决方案,其中一个接口从另一个接口继承:
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++ 中常量的编译时检查。正如埃里克·利珀特(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
接口访问实例,则只能向堆栈添加条目。您可以通过这种方式使用接口来获得与您所要求的类似的东西。引用类型(对象)参数默认为 IN 参数。 但因为它们是引用,所以它们的方法副作用和属性访问是在方法外部对对象完成的。 该对象不必传递出去。 还是按照方法修改的。
但是,默认情况下,值类型(结构)参数也是 IN,并且不能对传入的元素产生副作用或属性修改。相反,它会在进入方法之前获得 COPIED ON WRITE。 当方法超出范围(方法结束)时,对该方法内部的任何更改都会终止。
不要仅仅为了满足这种需求而将类更改为结构。 这是一个坏主意。 但如果它们无论如何都应该是结构体,现在你就会知道了。
顺便说一句,一半的编程社区并没有正确理解这个概念,但他们认为他们理解了(事实上,我在几本书中发现了 C# 中参数方向问题的不准确之处)。 如果您想对我的陈述的准确性发表评论,请仔细检查以确保您知道自己在说什么。