问题
I'm overriding a property in my derived class that I would like to make it readonly. The C# compiler won't let me change the access modifiers, so it must stay public.
What's the best way to do this? Should I just throw an InvalidOperationException
in set { }
?
回答1:
Having the setter throw an InvalidOperationException
in a derived class violates the Liskov Subsitution Principle. Essentially makes the usage of the setter contextual to the type of the base class which essentially eliminates the value of polymorphism.
Your derived class must respect the contract of it's base class. If the setter is not appropriate in all circumstances then it doesn't belong on the base class.
One way to work around this is to break the hierarchy up a little bit.
class C1 {
public virtual int ReadOnlyProperty { get; }
}
class C2 {
public sealed override int ReadOnlyProperty {
get { return Property; }
}
public int Property {
get { ... }
set { ... }
}
}
Your type you're having problems with could inherit C1
in this scenario and the rest could switch to derive from C2
回答2:
You can hide the original implementation and return the base implementation:
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
}
}
}
However,. as Jared has alluded to, this is kind of a weird situation. Why don't you make your setter private or protected in the base class to begin with?
回答3:
I think i this situation the better solution is new keyword
public class Aclass {
public int ReadOnly { get; set; }
}
public class Bclass : Aclass {
public new int ReadOnly { get; private set; }
}
回答4:
You're not allowed to hide your base class' public members in C#, because somebody could take an instance of your derived class, stuff it into a reference to the base class, and access the property that way.
If you want to make a class that provided most, but not all, of the interface of another class, you'll have to use aggregation. Don't inherit from the "base class," just include one in the "derived" one by value, and provide your own member functions for the portions of the "base class" functionality you wish to expose. Those functions can then just forward the calls on to the appropriate "base class" functions.
回答5:
Just define the property with new keyword and don provide the Setter method in the derived class. This will hide the base class property as well, however as mjfgates mentioned one can still access the base class property by assigning it to a base class instance. Thats polymorphism.
回答6:
There are some cases where the pattern you describe is justifiable. For example, it may be helpful to have an abstract class MaybeMutableFoo
, from which are derived subtypes MutableFoo
and ImmutableFoo
. All three classes include an IsMutable
property, and methods AsMutable()
, AsNewMutable()
, and AsImmutable()
.
In that situation, it would be entirely proper for the MaybeMutableFoo
to expose a read-write property, if its contract explicitly specifies that the setter may not work unless IsMutable
returns true. An object which has a field of type MaybeMutableFoo
that happens to hold an instance of ImmutableFoo
could be perfectly happy with that instance unless or until it had to write to it, whereupon it would replace the field with an object returned via AsMutable()
and then use it as a mutable foo (it would know it was mutable, since it had just replaced it). Having MaybeMutableFoo
include a setter would avoid the need to do any future typecasts on the field once it was made to refer to a mutable instance.
The best way to allow for such a pattern is to avoid implementing virtual or abstract properties, but instead implement non-virtual properties whose getters and setters chain to virtual or abstract methods. If one has a base class
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};
}
then a derived class ImmutableFoo
may declare:
new public string Foo {get {return Foo_get();}
to make its Foo
property be read-only without interfering with the ability to code overrides for the abstract Foo_get()
and Foo_set()
methods. Note that the read-only replacement of Foo
doesn't change the behavior of the property get; the read-only version chains to same the base-class method as the base-class property did. Doing things this way will ensure that there's one patch point for changing the property getter, and one for changing the setter, and those patch points won't change even if the property itself gets redefined.
来源:https://stackoverflow.com/questions/4403925/make-property-readonly-in-derived-class