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.