9

I have a situation like this:

public abstract class BaseClass 
{
   public abstract string MyProp { get; }
}

Now, for some of the derived classes, the properties value is a synthesized values, so there is no setter:

public class Derived1 : BaseClass
{
    public override string MyProp { get { return "no backing store"; } }
}

This works fine. However, some of the derived class required a more traditional backing store. But, no matter how I write it, as on automatic property, or with an explicit backing store, I get an error:

public class Derived2 : BaseClass
{
    public override string MyProp { get; private set;}
}

public class Derived3 : BaseClass
{
    private string myProp;
    public override string MyProp 
    { 
        get { return myProp;} 
        private set { myProp = value;}
    }
}

Derived2.MyProp.set': cannot override because 'BaseClass.MyProp' does not have an overridable set accessor

How do I get this to work??

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 1
    It would work if BaseClass was an interface rather than an abstract class, which would be suitable for this contrived example, but perhaps not in your actual code. – Mud Nov 11 '10 at 05:37
  • True, BaseClass needs to be a class. It has implemented methods, which I did not show. – James Curran Nov 11 '10 at 16:27
  • Possible duplicate of [Why is it impossible to override a getter-only property and add a setter?](https://stackoverflow.com/questions/82437/why-is-it-impossible-to-override-a-getter-only-property-and-add-a-setter) – Neuron Oct 27 '17 at 16:02
  • @LonelyNeuron - Not exactly. That question asked for a justifaction. This one asks for a workaround. – James Curran Oct 27 '17 at 21:05

4 Answers4

11

The best thing you can do is implement the property as virtual instead of abstract. Make the get and set blocks for each throw NotSupportedException in the base class and override the behaviour accordingly in derived classes:

public virtual string MyProp {
    get {
        throw new NotSupportedException();
    }
    set {
        throw new NotSupportedException();
    }
}
Bradley Smith
  • 13,353
  • 4
  • 44
  • 57
  • 3
    What this doesn't provide you with is a compile time message if you forget to implement it in the derived class. You can implement get and set in the abstract, and throw not implemented explicitly in the derived class to show that it has been considered. – Chris Aug 16 '13 at 17:47
4

Basically, you cannot. By adding a setter you are changing the definition of the property, so it does not really "override" the base property. It's the same as if you tried to override a method and add another parameter to it - they would be treated as different methods (overloaded). Since properties cannot be overloaded this won't work.

You'll just have to add another method to set the value (perhaps with protected accessibility).

EMP
  • 59,148
  • 53
  • 164
  • 220
  • 2
    I don't think that's analogous. It's more like if `BaseClass` declared an abstract `GetFoo()` method and the derived class added a `SetFoo()` method -- it's extending it, but not altering it in such a way as to violate the LSP (unlike changing method signatures). – Ian Henry Nov 11 '10 at 05:43
  • I agree with you in theory, but the way it's been implemented in practice in .NET is that it *is* a change, because the property defines what accessors it has, separate to the definitions of the get_ and set_ methods that implement those accessors. – EMP Nov 11 '10 at 05:47
  • 1
    @Ian: I disagree. The abstraction that properties give us is to get/set properties of a class with the syntax of field access. Internally they are implemented as methods but that's besides the point. Defining a property, you are saying whether it can get and or set a value with varying accessibility. It does violate LSP since in one class, a property is defined to have only a get accessor then in a derived class, the same property as another accessor. – Jeff Mercado Nov 11 '10 at 05:52
  • @Evgeny - Good point, highlighting the difference between a `Property`, the .NET entity, and a "property" in the intuitive sense, a distinction which is very relevant for this question. @Jeff - I don't see how this violates the LSP. The only known property of `BaseClass` is that it has a getter. A derived class with a getter and a setter still has that getter. – Ian Henry Nov 11 '10 at 06:02
  • @Ian: It's not so much for simply adding a `set` accessor to the property but changing it's meaning entirely. A property with only a `get` accessor is thought of as being readonly. By adding a `set` accessor to that property in a derived class (publicly accessible or not) changes the meaning of that property thus violating LSP. It's like declaring a public constant or readonly field and later changing it in a derived class to be modifiable. – Jeff Mercado Nov 11 '10 at 07:14
  • 2
    @Jeff - That's not necessarily true. The value of a property with only a get accessor can easily change - think of computed properties. For example, `ICollection.Count` - you can't set it explicitly, but its value still changes. It's possible to imagine a subclass, `WeirdClass : ICollection`, that has a setter for `Count`, which either truncates the collection or adds some default value in order to make the number of items in the collection conform to your value. As long as `Count` represents the number of items in the collection, the correctness has not been impugned. – Ian Henry Nov 11 '10 at 15:31
  • @Ian: I'm not saying that reading from the value from a declared readonly property will not change, it certainly can. I'm saying that it can never be set directly by assigning to that property. It only has the get accessor defined making the property read (get) only. I don't know if I can explain it any better than that. Review what [MSDN](http://msdn.microsoft.com/en-us/library/x9fsa0sw.aspx) has to say on it. `Properties that do not implement a set accessor are read only.` – Jeff Mercado Nov 11 '10 at 20:08
0

I would suggest avoiding virtual or abstract properties. Instead, use a non-virtual property which chains to protected virtual or abstract get/set methods. Doing that will allow derived classes to override the methods and also shadow the property with one that has different access modifiers. Since the base property itself will be non-virtual, there will never be any need to override it, so the naming conflict with the new version won't matter.

supercat
  • 77,689
  • 9
  • 166
  • 211
0

Bradley's suggestion is good, but one thing I've done in cases where only the Setter should be virtual is to do something this this:

public class Root
{
    private string _MyProp;
    public string MyProp 
    {
        get { return _MyProp;}
        set { _MyProp = SetMyProp(value); }
    }
    protected virtual string SetMyProp(string suggestedValue)
    {
        return suggestedValue;
    }
}
public class Child
    : Root
{
    protected override string SetMyProp(string suggestedValue)
    {
        string oReturn = base.SetMyProp(suggestedValue);
        // Do some sort of cleanup here?
        return oReturn;
    }
}

It requires a little extra work up front, but it seems to maintain a higher degree of encapsulation (e.g. you can prevent subclasses from overriding the Getter behavior, and your subclass doesn't have to be aware of the underlying member behind the property).

Steven
  • 1,260
  • 9
  • 22
  • Although I agree that this is better practice in general, James' question specifically talks about properties which may not have an underlying member. Providing one in the base class is, in his scenario, wasteful since not all derived classes will require it. – Bradley Smith Nov 11 '10 at 15:17
  • You're right. I re-read his question, and I think I misunderstood things the first go-around. I was thinking he was just asking how to have a virtual setter...I missed the part about not necessarily having an exposed setter at all. – Steven Nov 11 '10 at 15:24