It's possible
tl;dr- You can override a get-only method with a setter if you want. It's basically just:
Create a new
property that has both a get
and a set
using the same name.
If you don't do anything else, then the old get
method will still be called when the derived class is called through its base type. To fix this, add an abstract
intermediate layer that uses override
on the old get
method to force it to return the new get
method's result.
This enables us to override properties with get
/set
even if they lacked one in their base definition.
As a bonus, you can also change the return type if you want.
If the base definition was get
-only, then you can use a more-derived return type.
If the base definition was set
-only, then you can us a less-derived return type.
If the base definition was already get
/set
, then:
In all cases, you can keep the same return type if you want. The below examples use the same return type for simplicity.
Situation: Pre-existing get
-only property
You have some class structure that you can't modify. Maybe it's just one class, or it's a pre-existing inheritance tree. Whatever the case, you want to add a set
method to a property, but can't.
public abstract class A // Pre-existing class; can't modify
{
public abstract int X { get; } // You want a setter, but can't add it.
}
public class B : A // Pre-existing class; can't modify
{
public override int X { get { return 0; } }
}
Problem: Can't override
the get
-only with get
/set
You want to override
with a get
/set
property, but it won't compile.
public class C : B
{
private int _x;
public override int X
{
get { return _x; }
set { _x = value; } // Won't compile
}
}
Solution: Use an abstract
intermediate layer
While you can't directly override
with a get
/set
property, you can:
Create a new
get
/set
property with the same name.
override
the old get
method with an accessor to the new get
method to ensure consistency.
So, first you write the abstract
intermediate layer:
public abstract class C : B
{
// Seal off the old getter. From now on, its only job
// is to alias the new getter in the base classes.
public sealed override int X { get { return this.XGetter; } }
protected abstract int XGetter { get; }
}
Then, you write the class that wouldn't compile earlier. It'll compile this time because you're not actually override
'ing the get
-only property; instead, you're replacing it using the new
keyword.
public class D : C
{
private int _x;
public new virtual int X { get { return this._x; } set { this._x = value; } }
// Ensure base classes (A,B,C) use the new get method.
protected sealed override int XGetter { get { return this.X; } }
}
Result: Everything works!
Obviously, this works-as-intended for D
.
var test = new D();
Print(test.X); // Prints "0", the default value of an int.
test.X = 7;
Print(test.X); // Prints "7", as intended.
Everything still works-as-intended when viewing D
as one of its base classes, e.g. A
or B
. But, the reason why it works might be a little less obvious.
var test = new D() as B;
//test.X = 7; // This won't compile, because test looks like a B,
// and B still doesn't provide a visible setter.
However, the base class definition of get
is still ultimately overridden by the derived class's definition of get
, so it's still completely consistent.
var test = new D();
Print(test.X); // Prints "0", the default value of an int.
var baseTest = test as A;
Print(test.X); // Prints "7", as intended.
Discussion
This method allows you to add set
methods to get
-only properties. You can also use it to do stuff like:
Change any property into a get
-only, set
-only, or get
-and-set
property, regardless of what it was in a base class.
Change the return type of a method in derived classes.
The main drawbacks are that there's more coding to do and an extra abstract class
in the inheritance tree. This can be a bit annoying with constructors that take parameters because those have to be copy/pasted in the intermediate layer.