Home > Software design >  How to add a set accessor to an inherited read-only property
How to add a set accessor to an inherited read-only property

Time:05-16

I have a question about overriding properties in C#. There already is a similar question here, but the answers are not satisfying for me. Let's say I have these classes:

class A
{
    public int Prop { get; }
}

class B : A
{
    public int Prop { get; set; }
}

Now as you can see I want to add a setter to the property Prop in a subclass. I came up with two solutions. The first one is making the property virtual and overriding it in class B like this:

class A
{
    public virtual int Prop { get; }
}

class B : A
{
    public override int Prop { get; set; }
}

But unfortunately the compiler doesn't allow me to do this. My second idea was to use a 'new' keyword:

class A
{
    public virtual int Prop { get; }
}

class B : A
{
    public new int Prop { get; set; }
}

Now everything seemingly works, but it's not a satisfying solution for me because of one detail. Let's consider for instance this piece of code:

B b = new B();
b.Prop = 5;
A a = b;
Console.WriteLine(a.Prop);

You probably know that I get 0 here in my output, but I want to get 5. Is there any way to solve this problem?

CodePudding user response:

You could try to explicitly implement property's setter. This requires an interface:

public interface IPropSet
{
    int Prop { set; }
}

class A
{
    public int Prop { get; }
}

class B : A, IPropSet
{
    int IPropSet.Prop { set { } }
}

But unfortunately this property cannot be set without casting to IPropSet:

((IPropSet)new B()).Prop = 1;

CodePudding user response:

A is not a subclass of B, this is not how inheritance works. I don't understand why do you want to do this, I think you are mistaken the complete concept of inheritance

In the case you proposes:

 class A
{
    public int Prop { get; }
}

class B : A
{
    public int Prop { get; set;  }
}

they are the same property. If you do a:

 class A
{
    public int Prop { get; }
}

class B : A
{
    public int AnotherProp { get; set;  }
}

you can set:

B instance = new B();
B.Prop = 5;

If what you want to do is having a property in a subclass with the same name (not recommended) you can do this, without working with inheritance:

internal class A
{
    internal int Prop { get; }
}

internal class B 
{
    internal A MySubClass { get; set;  }
}

B instanceB = new B();
A instanceA = new A();
instanceA.Prop = 5;
B.MySubClass = instanceA;

or you can play with that, without copy it directly. Is just an example to show you the difference between class as property and inheritance

CodePudding user response:

On Console.WriteLine(a.Prop); you reference to A.Prop, new in B class for Prop is for redefining.

When used as a declaration modifier, the new keyword explicitly hides a member that is inherited from a base class. When you hide an inherited member, the derived version of the member replaces the base class version. This assumes that the base class version of the member is visible, as it would already be hidden if it were marked as private or, in some cases, internal. Although you can hide public or protected members without using the new modifier, you get a compiler warning. If you use new to explicitly hide a member, it suppresses this warning. enter image description here

but you can use,

if (a is B bb)
{
    Console.WriteLine(bb.Prop);
}

Or use this pattern

class A
{
    public virtual int Prop { get; protected set; }
}
class B : A
{
     public void SetProp(int prop) => Prop = prop;
}


B b = new B();
b.SetProp(5);
A a = b;
Console.WriteLine(a.Prop);

CodePudding user response:

Property's set-ter in class A should be protected. You will still be able to override it and it still won't be accessible outside class A and its children.

Edit: I've read your question once again and tested my edited answer in Visual Studio (I haven't had a chance to do it the first time).

As I understand, besides overriding the property, you want to be able to set property's value on a subclass, from outside the class, but not setting it on the base class.

You could try something like this:

using System;

namespace PropertyInheritance
{
    public class Program
    {
        static void Main(string[] args)
        {
            A a1 = new A();
            B b1 = new B();

            a1.Prop = 1;
            b1.Prop = 2;

            Console.WriteLine($"a1.Prop = {a1.Prop}");
            Console.WriteLine($"b1.Prop = {b1.Prop}");

            Console.WriteLine($"a1.GetPropField() = {a1.GetPropField()}");
            Console.WriteLine($"b1.GetPropField() = {b1.GetPropField()}");

            B b2 = new B();
            b2.Prop = 5;
            A a2 = b2;
            Console.WriteLine($"a2.Prop = {a2.Prop}");

            Console.WriteLine($"a2.GetPropField() = {a2.GetPropField()}");

            Console.ReadLine();
        }
    }

    public class A
    {
        protected int prop;

        public virtual int Prop {
            get { return prop; }
            set { }
        }

        // test method - to check field's value
        public int GetPropField() => prop;
    }

    public class B : A
    {
        public override int Prop
        {
            get { return prop; }
            set { prop = value; }
        }
    }
}

Here is the output:

enter image description here

I suppose everything is clear, but if not, please feel free to ask. Maybe I still didn't understand the question, or even made some mistake in my answer.

  • Related