我正在覆盖我的派生类中的一个属性,我想让它只读。 C#编译器不允许我更改访问修饰符,因此它必须保持公开。
最好的方法是什么?我应该在InvalidOperationException
中抛出set { }
吗?
答案 0 :(得分:8)
让setter在派生类中抛出InvalidOperationException
会违反Liskov Subsitution Principle。本质上是将setter contextual用于基类的类型,这基本上消除了多态的价值。
您的派生类必须尊重它的基类合同。如果setter在所有情况下都不合适,那么它不属于基类。
解决这个问题的一种方法是稍微破坏层次结构。
class C1 {
public virtual int ReadOnlyProperty { get; }
}
class C2 {
public sealed override int ReadOnlyProperty {
get { return Property; }
}
public int Property {
get { ... }
set { ... }
}
}
您遇到问题的类型可以在此方案中继承C1
,其余类型可以转换为从C2
答案 1 :(得分:3)
您可以隐藏原始实现并返回基本实现:
class Foo
{
private string _someString;
public virtual string SomeString
{
get { return _someString; }
set { _someString = value; }
}
}
class Bar : Foo
{
public new string SomeString
{
get { return base.SomeString; }
private set { base.SomeString = value; }
}
}
namespace ConsoleApplication3
{
class Program
{
static void Main( string[] args )
{
Foo f = new Foo();
f.SomeString = "whatever"; // works
Bar b = new Bar();
b.SomeString = "Whatever"; // error
}
}
}
然而,。正如贾里德所暗示的那样,这是一种奇怪的情况。为什么不首先让你的setter私有或在基类中受保护?
答案 2 :(得分:1)
我认为在这种情况下,更好的解决方案是新关键字
public class Aclass {
public int ReadOnly { get; set; }
}
public class Bclass : Aclass {
public new int ReadOnly { get; private set; }
}
答案 3 :(得分:0)
您不能在C#中隐藏基类的公共成员,因为有人可以获取派生类的实例,将其填充到对基类的引用中,并以这种方式访问属性。
如果你想创建一个提供另一个类的接口的大部分但不是全部的类,你将不得不使用聚合。不要继承“基类”,只需在值“派生”中包含一个,并为要公开的“基类”功能部分提供自己的成员函数。然后,这些函数可以将调用转发到适当的“基类”函数。
答案 4 :(得分:0)
只需使用new关键字定义属性,并在派生类中提供Setter方法。这也将隐藏基类属性,但是正如mjfgates所提到的,仍然可以通过将基类属性分配给基类实例来访问基类属性。多态性。
答案 5 :(得分:0)
在某些情况下,您描述的模式是合理的。例如,拥有一个抽象类MaybeMutableFoo
可能会有所帮助,派生类型MutableFoo
和ImmutableFoo
。这三个类都包含IsMutable
属性,方法AsMutable()
,AsNewMutable()
和AsImmutable()
。
在这种情况下,MaybeMutableFoo
公开读写属性是完全正确的,如果它的契约明确指定setter可能不起作用,除非IsMutable
返回true。具有类型MaybeMutableFoo
的字段恰好包含ImmutableFoo
实例的对象可能对该实例非常满意,除非或直到它必须写入它,然后它将用一个实例替换该字段对象通过AsMutable()
返回,然后将其用作可变foo(它会知道它是可变的,因为它刚刚替换它)。让MaybeMutableFoo
包含setter可以避免在引用可变实例时对字段进行任何未来的类型转换。
允许这种模式的最佳方法是避免实现虚拟或抽象属性,而是实现非虚拟属性,其getter和setter链接到虚拟或抽象方法。如果有一个基类
public class MaybeMutableFoo
{
public string Foo {get {return Foo_get();} set {Foo_set(value);}
protected abstract string Foo_get();
protected abstract void Foo_set(string value};
}
然后派生类ImmutableFoo
可以声明:
new public string Foo {get {return Foo_get();}
使其Foo
属性为只读,而不会干扰抽象Foo_get()
和Foo_set()
方法的代码覆盖功能。请注意,Foo
的只读替换不会更改属性get的行为;只读版本链与基类属性相同的基类方法。以这种方式做事将确保有一个补丁点用于更改属性getter,另一个用于更改setter,即使属性本身被重新定义,这些补丁点也不会改变。