15
votes

What's the best way to initialize constants or other fields in inherited classes? I realize there are many syntax errors in this example, but this is the best example to explain clearly what I am trying to do.

public abstract class Animal {
  public abstract const string Name; // #1
  public abstract const bool CanFly;
  public abstract double Price; // price is not const, because it can be modified

  public void Fly() {
    if (!CanFly)
      Debug.Writeln("{0}s can't fly.", Name);
    else
      Debug.Writeln("The {0} flew.", Name);
  }
}

public class Dog : Animal {
  public override const string Name = "Dog"; // #2
  public override const bool CanFly = false;
  public override double Price = 320.0;
}

public class Bird : Animal {
  public override const string Name = "Bird";
  public override const bool CanFly = true;
  public override double Price = 43.0;
}

A few things I'm trying to accomplish:

  • Base classes must assign these 3 fields.
  • Ideally I would want these initialized fields to be together at the top of the class so I can see which constants I assigned to each class and change them whenever needed.
  • The fields Name and CanFly cannot be changed.

I know that you could initialize these fields in a constructor (if you get rid of the const), but then they aren't guaranteed to be assigned. If you have these fields as properties instead and override them, you would still need to initialize the backing field of the property. How would you implement this?

A few syntax errors it complains about:

  • The modifier 'abstract' is not valid on fields. Try using a property instead. (#1)
  • A const field requires a value to be provided (#1)
  • The modifier 'override' is not valid for this item (#2)
5
I think you're misunderstanding what constants are for. Constants are for values that never change throughout the history of the universe; hence, the name "constant". Good examples of constants: the number of eggs in a dozen, the atomic number of lead. Bad: the version number of your application; this changes over time. Terrible: the price of a barrel of oil; changes every few seconds. If there's a field whose value can change during the lifetime of your application, its not a constant. - Eric Lippert
Thanks for the explanation. I guess I was a bit confused because the Dog.Name and Dog.CanFly fields should never be changed, thus I would think they should be constants. However, the Animal.Name and Animal.CanFly fields will be changed by inherited classes, so they shouldn't be constants. I was trying to define them as constants in inherited classes only, which I guess you can't do. - Senseful
To model a value that is set once at construction time and never changed after that, use a readonly field. Readonly fields can be instance fields, in which case you can have a different value for each instance, or static, in which case every instance shares the same value. - Eric Lippert

5 Answers

22
votes

If a base class requires a value to be provided by a derived class, the two most common ways are:

Require it in the constructor:

public readonly double Price;

protected BaseClass(double price)
{
    this.Price = price;
}

Derived classes must pass the price into the constructor:

public Derived() : base(32)
{
}

Or, make it an abstract property:

public abstract double Price { get; }

Derived classes must provide some way to return the value (though where they get it is up to the derived class, which in many cases provides more flexibility):

public override double Price
{
    get
    {
        return 32;
    }
}
5
votes

If you want to have particular fields in your abstract class, but don't want to define them in the base class, you can require that implementers supply the values through the constructor.

public abstract class MyClass
{
    private readonly double price;

    protected MyClass(double price)
    {
        this.price = price
    }
}

With such a base class, all derived classes must supply a value to the base class' constructor, or the code will not compile. Here's one example of how that might look:

public class Foo : MyClass
{
    public Foo() : base(42) { }
}
3
votes

The errors you get are more or less telling you what to do. You want to use abstract properties, not fields, for Name and CanFly, while Price would be a normal property. In the derived classes, the abstract properties would be read-only and return a constant, in each case. In code:

public abstract class Animal {
  public abstract string Name { get; }
  public abstract bool CanFly { get; }
  public double Price { get; set; }
  // etc
}

public class Dog : Animal {
  public override string Name { get { return "Dog"; } }
  public override bool CanFly { get { return false; } }
  public Dog() { Price = 320.0; }
}

// etc
1
votes

You could use readonly and a internal constructor to enforce this behavior but it would be somewhat lacking in finese. If you go with this the inheritors would need to be in the same namespace/assembly in order to take advantage of the internal constructor.

0
votes

the other model is to create an accessor (property) that is virtual. THis forces the derived classes to implement it.

THe advantage of this is that 'canfly' might have an answer that depends on the (say) time of day.