12

In a base class I have this property:

public virtual string Text 
{
    get { return text; }
}

I want to override that and return a different text, but I would also like to be able to set the text, so I did this:

public override string Text
{
    get { return differentText; }
    set { differentText = value; }
}

This however does not work. I get a red squiggly under set saying that I can not override because it does not have a set accessor. Why is this aproblem? What should I do?

Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
Svish
  • 152,914
  • 173
  • 462
  • 620
  • 1
    I would like to do exactly the same. Should be possible. – char m Feb 27 '15 at 13:20
  • It is possible but requires a "middle-class". Also it's probably not a good idea in many situations, but there are some exceptions, for instance when overriding abstract properties. Base: `public abstract string Text { get; }`. Middle (inherits base): `protected internal string text;` `public sealed override string Text { get { return text; } }`. Child (inherits middle): `public new string Text { get { return text; } set { text = value; } }`. The protected internal modifier and the sealed override ensures encapsulation is not broken outside of your assembly. – AnorZaken Oct 11 '15 at 18:31
  • @AnorZaken: That's not overriding, that's shadowing. It doesn't require a "middle" class either. – Guffa Oct 11 '15 at 19:46
  • @Guffa It is both overriding and shadowing, and since c# doesn't allow both in the same class the middle class _is_ needed. – AnorZaken Oct 11 '15 at 22:03
  • @AnorZaken: It can't be both, shadowing isn't overriding. Overriding it in a middle class doesn't make it overridden in the child class, it's still shadowed. – Guffa Oct 11 '15 at 22:06
  • @Guffa Of course. I never said it was doing both in the same class... I said you can't do both in the same class and you reply with "you can't do both in the same class"...? This is most probably what the OP wanted to do, and it _simulates_ both overriding and shadowing thanks to the protected internal field. And no it would not work across assemblies, and no it isn't technically doing both, but that wasn't anything the OP asked for. – AnorZaken Oct 11 '15 at 22:11
  • @AnorZaken: No, it doesn't simulate overriding. Shadowing is just shadowing, having a middle class doesn't change that, and it's not needed at all to shadow the property. – Guffa Oct 11 '15 at 22:20
  • @Guffa Look closer at the code. The shadowing being done will _behave indistinguishably_ from overriding (which fits the definition of _simulates_ spot on) and this behavior can not be made to compile without the middle class, period. I challenge you to write code which _behaves exactly_ like it was overridden _but_ adds a set-accessor that doesn't require a middle class. If you actually try it you will find it's impossible. If you think my code is incorrect I challenge you to write a test that shows my claims are false. Unless you use reflection you will find that is impossible too. – AnorZaken Oct 12 '15 at 08:08
  • I wish this worked! `public override int? OrderID { get; new set; }` – Simon_Weaver Apr 28 '22 at 19:00

5 Answers5

6
public virtual string Text 
{
    get { return text; }
    protected set {}
}

change base class property like this, you are trying to override set method that doesn't exist

Arsen Mkrtchyan
  • 49,896
  • 32
  • 148
  • 184
5

In your second block of code you are creating a public set method, but the word "override" in the declaration makes the compiler look for a method with the same signature in the base class. Since it can't find that method it will not allow you create your set.

As ArsenMkrt says you could change your base declaration to contain a protected set. This will allow you to override it, but since you still won't be able to change the signature you can't promote this method to public in your subclass, so the code you posted still won't work.

Instead you either need to add a public virtual set method to your base class that doesn't do anything (or even throws an exception if you try and call it) but this goes against what a user of the class would expect the behaviour to be so if you do this (and I won't recommend it) make sure it is so well documented that the user can't miss it:

///<summary>
///Get the Text value of the object
///NOTE: Setting the value is not supported by this class but may be supported by child classes
///</summary>
public virtual string Text 
{
    get { return text; }
    set { }
}

//using the class

BaseClass.Text = "Wibble";
if (BaseClass.Text == "Wibble")
{
    //Won't get here (unless the default value is "Wibble")
}

Otherwise declare the set as a separate method in your child class:

public override string Text
{
    get { return differentText; }
}

public void SetText(string value)
{
    differentText = value;
}
Martin Harris
  • 28,277
  • 7
  • 90
  • 101
  • 1
    I don't like it, but I guess I will have to do something along the lines of having a separate SetText method or something... thanks :) – Svish Aug 04 '09 at 12:41
  • Well, it could also be an architectural issue. Why can't the user set the text value in the base class? Is it *really* the same property in both classes? It seems to be an usual requirement to need a pattern like this. – Martin Harris Aug 04 '09 at 12:46
  • Well, it is a number of different Parameter classes which holds different kinds of values. And I would like them all to be able to represent themselves as a text value that the user can read (for printing for example). But I don't want all the parameters to be set by text (parsing is a lot more annoying than printing). For example a date parameter I want to print the date, but I don't need to set the date through text, since the setting will be done with a datepicker. – Svish Aug 04 '09 at 13:14
3

You want more capabilities to be exposed when using a child type. It sounds like you don't want to override, you want to shadow. Just use the new keyword to hide the readonly Text property under your readable/writable property.

In base class:

protected string text;
public string Text 
{
    get { return text; }
}

In derived class:

new public string Text 
{
    get { return text; }
    set { text = value; }
}
Craig Gidney
  • 17,763
  • 5
  • 68
  • 136
  • Can you shadow and override at the same time? – Svish Aug 04 '09 at 13:39
  • No, they mean different things. Whether you shadow or override determines whether the behavior of Text is affected even after casting to the base type. Check out this question for a better explanation: http://stackoverflow.com/questions/673779/what-is-shadowing – Craig Gidney Aug 04 '09 at 13:48
2

It's a problem because you are breaking the encapsulation. You can't override something and make it more accessible, that would throw everything about encapsualtion out the window.

That's the rule and it applies in your case also, eventhough you are actually exposing something that is not the original value.

There is no way to do exactly what you attempted. You have to either make a setter in the base class, or use a different method of setting the new value.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • 1
    The child-class can't break encapsulation because what he is doing can not be done unless encapsulation was already broken in the base-class already (if the child is not supposed to be able to access the backing field of the property). Also the base-class has declared the property as virtual which means it is supposed to be possible to alter its behavior - and when a property/method is virtual anything goes! (As long as you adhere to its documentation of course.) Whether or not it _should be_ virtual, or if that breaks encapsulation of the base-class, is the real question, and that depends. – AnorZaken Oct 11 '15 at 18:10
  • @AnorZaken: What are you talking about? Just because a member is virtual doesn't mean that you can override it any way you like. – Guffa Oct 11 '15 at 19:52
2

You could hide the property from the base class :

public new string Text
{
    get { return differentText; }
    set { differentText = value; }
}

But in that case that property will only be used when manipulating the object through a variable of this type, not the base type

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758