Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Introduce setter for some - but not all - inherited classes

I am having a hard time implementing a property in C# that only has a getter in the abstract base class, but where I need to introduce a setter in one of the derived classes.


Update: For a shorter explanation of a generalized example of this question, see this question. The selected answer has explained why this is currently impossible to do in C#, however, in my mind no satisfactory solution has yet been provided.


An overview of my class diagram is shown below:

Class diagram

My objective is that the two classes TextElementStatic and TextElementReferenceSource should have a Text property with both getters and setters, while the class TextElementReferenceTarget should have a Text property with only a getter. I'm constantly using ITextElement while referencing all of these objects, and I need to ensure that the ITextElement interface only has a getter. Also, the base class TextElement implements a lot of common code, so all classes need to inherit from that class.

My current code looks like this:

Interface: ITextElement

public interface ITextElement
{
    string Text { get; }
}

Interface: ITextElementUpdatable

public interface ITextElementUpdatable : ITextElement
{
    new string Text { get; set; }
}

Abstract class: TextElement (This is where my problem is, explained below)

public abstract class TextElement : ITextElement
{
    // I want to mark this 'abstract', but that causes my problem
    public virtual string Text
    {
        get
        {
            // NOTE: This should never be called
            Debug.Fail("Called virtual Text getter that should never be called");
            return default(string);
        }
    }
}

Abstract class: TextElementUpdatable

public abstract class TextElementUpdatable : TextElement, ITextElementUpdatable
{
    // Should have both a getter and a setter  
    public new virtual string Text { get; set; }
}

Class: TextElementStatic

public class TextElementStatic : TextElementUpdatable
{
    // Should have both a getter and a setter
    // No Text property declaration
    // Inherits Text property from TextElementUpdatable
}

Class: TextElementReferenceSource

public class TextElementReferenceSource : TextElementUpdatable
{      
    // Should have both a getter and a setter     
    public override string Text
    {
        get { return _internalobject.Text; }
        set { _internalobject.Text = value; }
    }
}

Class: TextElementReferenceTarget

public class TextElementReferenceTarget : TextElement
{
    // Should ONLY have a getter
    public override string Text
    {
        get { return _internalobject.Text; }
    }
}

So, my issue is: I really want to declare the Text property in the base class TextElement abstract, because it should always be implemented in the derived classes (both TextElementUpdatable, TextElementReferenceSource and TextElementReferenceTarget implements this property). However, if I try to convert the property to public abstract string Text { get; }, then I receive an error in TextElementUpdatable specifying that

TextElementUpdatable.Text hides the inherited property TextElement.Text

Further, if I change the property in TextElementUpdatable from new to override the error message is replaced by:

Cannot override because TextElement.Text does not have an overridable set accessor

Now, I could go back to TextElement and change the property to public virtual string Text { get; private set; } and call it a day, since that method should never be called anyway (which is basically the solution I have now). However, if I or someone create another derived class later on, I want to force me/them to implement the Text-property, hence I would rather mark it abstract than provide a virtual implementation.

Any suggestions on how I can do this the right way - even if it should involve a lot of refactoring?

I know that I could separate the two objectives her, providing one inherited Text property with only a getter, and then introduce a SetText() method in the ITextElementUpdatable interface. However, I'm wondering whether it is possible to find a good solution with properties only.


Another similar question, but without any answers I've been able to use: C# - What should I do when every inherited class needs getter from base class, but setter only for ONE inherited class

like image 526
Gedde Avatar asked May 23 '14 15:05

Gedde


1 Answers

It is really an exciting desing problem, but.. You have to use the new keyword what is not a good practice. Try to avoid them. Of course, property names can be the same in the interfaces, but if both implemented by a class (and one of the props defined without a setter), we have to implement them explicitelly. We have to accept that these properties "conflict". You could introduce abstract methods:

public abstract class TextElement : ITextElement
{
    public string Text { get { return GetText(); } }

    protected abstract string GetText();
}

public abstract class TextElementUpdatable : TextElement, ITextElementUpdatable
{
    string ITextElementUpdatable.Text
    {
        get { return GetText(); }
        set { SetText(value); }
    }

    protected abstract void SetText(string text);
}
like image 120
user3636971 Avatar answered Oct 28 '22 01:10

user3636971