61

I'm trying to create an abstract class that defines a property with a getter. I want to leave it up to derived classes to decide if they want to implement a setter for the property or not. Is this possible?

What I have so far:

public abstract class AbstractClass {
    public abstract string Value { get; }
    public void DoSomething() {
        Console.WriteLine(Value);
    }
}

public class ConcreteClass1 : AbstractClass {
    public override string Value { get; set; }
}

public class ConcreteClass2 : AbstractClass {
    private string _value;
    public override string Value {
        get { return _value; }
    }
    public string Value {
        set { _value = value; }
    }
}

public class ConcreteClass3 : AbstractClass {
    private string _value;
    public override string Value {
        get { return _value; }
    }
    public void set_Value(string value) {
        _value = value;
    }
}

In ConcreteClass1, I get an error on the set. It can't override set_Value because no overridable set accessor exists in AbstractClass.

In ConcreteClass2, I get an error on both Value's because a member with the same name is already declared.

ConcreteClass3 doesn't give an error, but even though Value's set accessor would be compiled into set_Value, it doesn't work the other way around. Defining a set_Value does not mean that Value gets a set accessor. So I can't assign a value to a ConcreteClass3.Value property. I can use ConcreteClass3.set_Value("value"), but that's not what I'm trying to achieve here.

Is it possible to have the abstract class demand a public getter, while allowing an optional setter to be defined in a derived class?

In case you'r wondering, this is just a theoretical question. I don't have a real situation where something like this is needed. But I can imagine an abstract class that doesn't care how a property gets set, but that does need to be able to get the property.

Kalamarico
  • 5,466
  • 22
  • 53
  • 70
comecme
  • 6,086
  • 10
  • 39
  • 67

4 Answers4

47

Unfortunately, you can't do exactly what you want. You can do this with interfaces though:

public interface IInterface {
    string MyProperty { get; }
}

public class Class : IInterface {
    public string MyProperty { get; set; }
}

The way I would do it is to have a separate SetProperty method in the concrete classes:

public abstract class AbstractClass {
    public abstract string Value { get; }
}

public class ConcreteClass : AbstractClass {

    private string m_Value;
    public override string Value {
        get { return m_Value; }
    }

    public void SetValue(string value) {
        m_Value = value;
    }
}
thecoop
  • 45,220
  • 19
  • 132
  • 189
  • Using the `interface`, I can't implement the `DoSomething` method in my base class (there is no base class, just an interface). Using `SetValue` is the same as the `set_Value` in my `ConcreteClass3`. But if that's the way to go, naming it `SetValue` is probably better than `set_Value`. – comecme Jan 10 '11 at 11:34
  • 1
    The reason set_Value isn't linked to the property is there's an explicit `PropertyDef` definition in the assembly that links the `get_` and `set_` methods; simply naming a method `set_` doesn't link it to a property. There's no way to do exactly what you want in C#; you will have to compromise. – thecoop Jan 10 '11 at 11:41
  • 1
    You can implement `DoSomething` as an extension method on `IInterface`. See my edit. – piedar Jul 12 '13 at 14:32
  • Does this difference in the way derived properties work between abstract classes and interfaces stem from the Common Language Specification or is it implementation specific? – Fredrick Aug 15 '13 at 13:47
  • Although highly voted this solution is completely useless if the only reason you need a `set` is to make it work with a property grid which doesn't understand methods. – Stelios Adamantidis Jun 02 '21 at 15:43
7

Found a solution: How to override a getter-only property with a setter in C#?

public abstract class A
{
    public abstract int X { get; }
}
public class B : A
{
    public override int X { get { return 0; } }
}
/*public class C : B  //won't compile: can't override with setter
{
    private int _x;
    public override int X { get { return _x; } set { _x = value; } }
}*/
public abstract class C : B  //abstract intermediate layer
{
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}
public class D : C  //does same thing, but will compile
{
    private int _x;
    protected sealed override int XGetter { get { return this.X; } }
    public new virtual int X { get { return this._x; } set { this._x = value; } }
}

D is now equivalent to a class inheriting from B while also being able to override in a setter.

Community
  • 1
  • 1
Nat
  • 1,085
  • 2
  • 18
  • 35
  • 4
    While it seems to work technically it does look a bit over-complicated compared to simplicity of the accepted answer. Can't imagine convincing my team to use it just to have some properties settable for testing purpose :) But have an up vote for the effort anyway. – Sevenate Nov 18 '20 at 02:51
3

You can just use protected access modifier instead. Because of inheritance you are not allowed to use private. It looks like so:

public abstract class A
{
    public abstract int prop { get; protected set; }
}

public abstract class B : A
{
    public override int prop { get; protected set; }
}
  • I think you left out the half part of the answer, where the class can set a value to _prop_. – Kristianne Nerona Sep 08 '20 at 22:30
  • How would the derived class (`B`) be able to implement a public setter for `prop`? – comecme Sep 11 '20 at 06:00
  • @comecme, just leave it like `public int prop { get; set; }` – Владик Sep 14 '20 at 19:48
  • 1
    If class A has `{get; protected set;}`, and I create class B with an `public override int prop {get; protected set;}` it does not compile. I can't override the property with a protected setter by one with a public setter. – comecme Sep 24 '20 at 15:17
0

Not very elegant, but it's the closest you can get without doing something like you have in concreteclass3

public class Concrete : AbstractClass
{
    public new void DoSomething()
    {
        Console.WriteLine(Value);
    }
}

public abstract class AbstractClass
{
    protected AbstractClass()
    {
        try
        {
            var value = Value;
        }
        catch (NotImplementedException)
        {
            throw new Exception("Value's getter must be overriden in base class");
        }
    }
    public void DoSomething()
    {
        Console.WriteLine(Value);
    }

    /// <summary>
    /// Must be override in subclass
    /// </summary>
    public string Value { get { throw new NotImplementedException(); } }
}
Rob
  • 26,989
  • 16
  • 82
  • 98
  • I don't see why you define a `new DoSomething` in the concrete class. Also, you'r solution will throw an exception at runtime, at compile time the absence of a Value implementation won't be noticed at all. – comecme Jan 10 '11 at 13:41