Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it impossible to override a getter-only property and add a setter? [closed]

People also ask

Can we override a property in C#?

In C# 8.0 and earlier, the return types of an override method and the overridden base method must be the same. You cannot override a non-virtual or static method. The overridden base method must be virtual , abstract , or override . An override declaration cannot change the accessibility of the virtual method.

Can we use setter without getter?

That code only requires the setter, not the getter. The interface documents that fact. An interface is just a facility for declaring a group of operations that are "atomically needed" (e.g. if you need to call method A, you'll need to read property B and set property C). So as always, it depends.

Is defined as a property in class but is overridden here in?

The error "'X' is defined as a property in class 'Y', but is overridden here in 'Z' as an accessor" occurs when a property overrides an accessor or vice versa in a derived class. To solve the error, use properties or accessors in both classes for the members you need to override.


I think the main reason is simply that the syntax is too explicit for this to work any other way. This code:

public override int MyProperty { get { ... } set { ... } }

is quite explicit that both the get and the set are overrides. There is no set in the base class, so the compiler complains. Just like you can't override a method that's not defined in the base class, you can't override a setter either.

You might say that the compiler should guess your intention and only apply the override to the method that can be overridden (i.e. the getter in this case), but this goes against one of the C# design principles - that the compiler must not guess your intentions, because it may guess wrong without you knowing.

I think the following syntax might do nicely, but as Eric Lippert keeps saying, implementing even a minor feature like this is still a major amount of effort...

public int MyProperty
{
    override get { ... } // not valid C#
    set { ... }
}

or, for autoimplemented properties,

public int MyProperty { override get; set; } // not valid C#

It's possible.

tl;dr You can override a get-only method with a setter if you want. It's basically just:

  1. Create a new property that has both a get and a set using the same name.

  2. override the prior get to alias the new get.

This enables us to override properties with get/set even if they lacked a setter in their base definition.


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:

  1. Create a new get/set property with the same name.

  2. 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!

var d = new D();

var a = d as A;
var b = d as B;
var c = d as C;

Print(a.X);      // Prints "0", the default value of an int.
Print(b.X);      // Prints "0", the default value of an int.
Print(c.X);      // Prints "0", the default value of an int.
Print(d.X);      // Prints "0", the default value of an int.

// a.X = 7;      // Won't compile: A.X doesn't have a setter.
// b.X = 7;      // Won't compile: B.X doesn't have a setter.
// c.X = 7;      // Won't compile: C.X doesn't have a setter.
d.X = 7;         // Compiles, because D.X does have a setter.

Print(a.X);      // Prints "7", because 7 was set through D.X.
Print(b.X);      // Prints "7", because 7 was set through D.X.
Print(c.X);      // Prints "7", because 7 was set through D.X.
Print(d.X);      // Prints "7", because 7 was set through D.X.

Discussion.

This method allows you to add set methods to get-only properties. You can also use it to do stuff like:

  1. Change any property into a get-only, set-only, or get-and-set property, regardless of what it was in a base class.

  2. 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.


Bonus: You can change the property's return-type.

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 use a less-derived return type.

  • If the base definition was already get/set, then:

    • you can use a more-derived return type if you make it set-only;

    • you can use a less-derived return type if you make it get-only.

In all cases, you can keep the same return type if you want.


I stumbled across the very same problem today and I think I have a very valid reason for wanting this.

First I'd like to argue that having a get-only property doesn't necessarily translate into read-only. I interpret it as "From this interface/abstract class you can get this value", that doesn't mean that some implementation of that interface/abstract class won't need the user/program to set this value explicitly. Abstract classes serve the purpose of implementing part of the needed functionality. I see absolutely no reason why an inherited class couldn't add a setter without violating any contracts.

The following is a simplified example of what I needed today. I ended up having to add a setter in my interface just to get around this. The reason for adding the setter and not adding, say, a SetProp method is that one particular implementation of the interface used DataContract/DataMember for serialization of Prop, which would have been made needlessly complicated if I had to add another property just for the purpose of serialization.

interface ITest
{
    // Other stuff
    string Prop { get; }
}

// Implements other stuff
abstract class ATest : ITest
{
    abstract public string Prop { get; }
}

// This implementation of ITest needs the user to set the value of Prop
class BTest : ATest
{
    string foo = "BTest";
    public override string Prop
    {
        get { return foo; }
        set { foo = value; } // Not allowed. 'BTest.Prop.set': cannot override because 'ATest.Prop' does not have an overridable set accessor
    }
}

// This implementation of ITest generates the value for Prop itself
class CTest : ATest
{
    string foo = "CTest";
    public override string Prop
    {
        get { return foo; }
        // set; // Not needed
    }
}

I know this is just a "my 2 cents" post, but I feel with the original poster and trying to rationalize that this is a good thing seems odd to me, especially considering that the same limitations doesn't apply when inheriting directly from an interface.

Also the mention about using new instead of override does not apply here, it simply doesn't work and even if it did it wouldn't give you the result wanted, namely a virtual getter as described by the interface.


I agree that not being able to override a getter in a derived type is an anti-pattern. Read-Only specifies lack of implementation, not a contract of a pure functional (implied by the top vote answer).

I suspect Microsoft had this limitation either because the same misconception was promoted, or perhaps because of simplifying grammar; though, now that scope can be applied to get or set individually, perhaps we can hope override can be too.

The misconception indicated by the top vote answer, that a read-only property should somehow be more "pure" than a read/write property is ridiculous. Simply look at many common read only properties in the framework; the value is not a constant / purely functional; for example, DateTime.Now is read-only, but anything but a pure functional value. An attempt to 'cache' a value of a read only property assuming it will return the same value next time is risky.

In any case, I've used one of the following strategies to overcome this limitation; both are less than perfect, but will allow you to limp beyond this language deficiency:

   class BaseType
   {
      public virtual T LastRequest { get {...} }
   }

   class DerivedTypeStrategy1
   {
      /// get or set the value returned by the LastRequest property.
      public bool T LastRequestValue { get; set; }

      public override T LastRequest { get { return LastRequestValue; } }
   }

   class DerivedTypeStrategy2
   {
      /// set the value returned by the LastRequest property.
      public bool SetLastRequest( T value ) { this._x = value; }

      public override T LastRequest { get { return _x; } }

      private bool _x;
   }

You could perhaps go around the problem by creating a new property:

public new int Bar 
{            
    get { return 0; }
    set {}        
}

int IBase.Bar { 
  get { return Bar; }
}