45

So I have a base class that has many children. This base class defines some readonly properties and variables that have default values. These can be different, depending on the child.

Readonly properties/fields allow you to change the value of the variable inside the constructor and also the definition, but nowhere else. I get a 'readonly variable can only be assigned to in a constructor' error if I try to change the value of an inherited readonly variable in the child class' constructor. Why is this and how can I work around this, without Reflection?

My intention: To allow user extensibility through scripts where they can only change certain fields once.

1
  • I had a very similar problem, and got an answer that allows you to make a single assignment to a member. Functionally readonly, and all the boilerplate code is in a single location. Commented Mar 9, 2017 at 2:27

6 Answers 6

39

The reason is that you can only assign to readonly fields in the constructor of that class.
According to the definition of readonly in the C# Reference (emphasis mine):

When a field declaration includes a readonly modifier, assignments to the fields introduced by the declaration can only occur as part of the declaration or in a constructor in the same class.

To work around this, you could make a protected constructor in the base that takes a parameter for the readonly property.

An example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Base b = new Child();
            Console.WriteLine(b.i);
            Console.Read();
        }
    }

    class Base
    {
        public readonly int i;

        public Base()
        {
            i = 42;
        }

        protected Base(int newI)
        {
            i = newI;
        }
    }

    class Child : Base
    {
        public Child()
            : base(43)
        {}
    }
}
2
  • 10
    The only drawback to this solution is that it doesn't allow you to compute the values to be passed to the base class.
    – Kent
    Commented Jan 22, 2016 at 4:49
  • 7
    @Kent: It is possible by calling a static method in the parameter list of the base class constructor. See stackoverflow.com/questions/1651444/…
    – ofthelit
    Commented Aug 8, 2016 at 8:49
12

You could get the exact behavior you are looking for by using virtual get only properties.

public class BSE
{
    virtual public int Prop 
    {
        get
        {
            return 6;
         }
     }
}
public class Derived : BSE
{
    public override int Prop
    {
         get
         {
             return 10;
         }
    }
 }

Fields are out side the inheritance and overloading model and should not be used to provide polymorphic features.

2
  • This sounds more like it; but it is still a rather large jump from the clean public readonly int number = 5. Commented May 18, 2011 at 5:41
  • @Ruirize You're assigning a constant to a readonly field? That really sounds like an abuse of readonly fields and that you might want to use virtual properties instead. Commented May 18, 2011 at 6:27
11

Adam has the right answer. If you're worried about the space it will take up (number of parameters in the constructor?) then you should address that as a different problem with a different solution: create a BaseConfig class, that contains all those properties and that is all that needs to be passed in. Base can then either assign all it's readonly fields from BaseConfig's properties, or you can instead have Base hold just one readonly field of type BaseConfig and refer to that for the values.

As to why this is, see C# constructor execution order regarding when each class's readonly fields would be initialized/initializable.

1
  • This makes me a little sad; I was hoping for a really nice way of implementing this, so that making scripts for my engine would be really neat and clever. Oh well. A BaseConfig class will work just as well, thanks! Commented May 18, 2011 at 6:34
2

You can use property with public get accessor and protected set accessor. Derived classes can set value of this property.

An example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Base b = new Child();
            Console.WriteLine(b.I);
            Console.Read();
        }
    }

    class Base
    {
        public int I { get; protected set; }

        public Base()
        {
            I = 42;
        }
    }

    class Child : Base
    {
        public Child()
        {
            I = 43;
        }
    }
}
1
  • 1
    protected is not readonly. The derived classes can set it outside of their constructors.
    – joe
    Commented Mar 3, 2020 at 4:41
0

this is impossible by design. try passing the values to a protected base class constructor

0
0

This is essentially what @rerun came up with, but with abstract instead of virtual:

protected abstract class BaseClass
{
    protected abstract int PrivateI { get; }
}

public class DerivedClass : BaseClass
{
    private readonly int _i;
    protected override int PrivateI => _i;

    public DerivedClass(int i)
    {
        _i = i;
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.