23

A readonly field should be used when you have a variable that will be known at object-instatiation which should not be changed afterwards.

However one is not allowed to assign readonly fields from constructors of subclasses. This doesn't even work if the superclass is abstract.

Does anyone have a good explanation why this either isn't a good idea, or lacks in the C# languange?

abstract class Super
{
    protected readonly int Field;
}

class Sub : Super 
{
    public Sub()
    {
        this.Field = 5; //Not compileable
    }
}

PS: You can of course reach the same result by having assignment of the readonly fields in a protected constructor in the superclass.

philipshield
  • 303
  • 2
  • 8
  • 1
    Also the compiler error isn't accurate: `Error 68 A readonly field cannot be assigned to (except in a constructor or a variable initializer)` – nicodemus13 Jan 30 '13 at 23:37

5 Answers5

14

The only reason I can see for this is because "it was just designed that way", as per the spec:

Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class.

The point of being read only is that it cannot be changed, if derived classes could modify then this would no longer be true and will violate encapsulation (by modifying the internals of another class).

m.edmondson
  • 30,382
  • 27
  • 123
  • 206
  • 1
    thats true. But is it still inappropriate for abstract classes? Perhaps because of the possibility of multiple subclasses. – philipshield Oct 09 '11 at 21:15
  • 2
    It possibly is inappropriate, and perhaps we'll never know the exact reason for that design decision. – m.edmondson Oct 09 '11 at 21:18
  • 1
    I don't see why it's inappropriate for abstract classes, since they can have constructors, therefore readonly fields would work. – pauloya Sep 13 '13 at 15:23
13
public class Father
{
    protected readonly Int32 field;

    protected Father (Int32 field)
    {
        this.field = field;
    }
}

public class Son : Father
{
    public Son() : base(5)
    {

    }
}

You may try something like this instead!

Renato Gama
  • 16,431
  • 12
  • 58
  • 92
  • Yes I mentioned that in my question :) Also, alexm posted the exact same answer about 10 minutes ago. – philipshield Oct 09 '11 at 21:20
  • 2
    It was not an edit, but nevermind that! It's a neat workaround – philipshield Oct 09 '11 at 21:21
  • 1
    exactly the solution I was looking for. This is just nitpicking, but one slight modification I would make just to reinforce the readonlyness (not a word): Declare a variable "private readonly Int32 CONCRETE_FIELD = 5" in the Son class. Then for the constructor "public Son() : base (CONCRETE_FIELD) { }. – Michael Kaldwid Jan 03 '17 at 16:26
2

I would model this by an abstract/virtual property in C#.

abstract class Super {
  protected abstract int Field { get; }
}

class Sub : Super {
  protected override int Field { get { return 5; } }
}

In my opinion that's a better solution than to have a constructor that includes each and every readonly field as parameter. For one because the compiler is able to inline this as well and also because the constructor solution will look like this in the derived class:

class Sub : Super {
  public Sub() : base(5) { } // 5 what ?? -> need to check definition of super class constructor
}

Also that may not work if you already have a constructor that takes a single int value.

Andreas
  • 6,447
  • 2
  • 34
  • 46
0

I would prefer to use the protected constructor in superclass (as mentioned by alexm), reachly with xml comments. This should eliminate the problem what DonAndre told in his code comment.

CLS
  • 571
  • 5
  • 10
0

I suppose the main reason is an additional complexity for all .NET language implementations

also, there is always a simple workaround:

 abstract class Super
 {
     protected readonly int Field;

     protected Super(int field)
     {
          this.Field = field;
     }
 }


class Sub : Super {
   public Sub():base(5)
   {
   }

}

alexm
  • 6,854
  • 20
  • 24