I'm trying to get a simple set of interfaces to be nested so I can enforce some members on derived classes.
Your main issue here is that defined the two interfaces as private
by default. That's what is preventing you from creating public List<IAbility> Abilities { get; set; }
inside MentalAbilities
.
Actually, the property public List<MentalAbility> Abilities { get; set; }
is also preventing you as you can't have a property of the same name defined twice.
The big question here would be what would be the point of allowing MentalAbilities
the ability to set a List<IAbility>
since that would mean setting any type of ability.
Ideally MentalAbilities
should only have a single list of MentalAbility
that it contains - and it should only be read-only. In fact, most of your interfaces should be read-only.
Here's what you should do:
Start with these interfaces:
public interface IAbilities<A> where A : IAbility
{
List<A> Abilities { get; }
}
public interface IAbility
{
Category Category { get; }
int Value { get; }
string Description { get; }
}
Notice the IAbilities<A>
interface uses a generic type A
that you can fill in later.
Next, let's set up some base classes that simplify the implementation of each specific class.
public abstract class AbilitiesBase<A> : IAbilities<A> where A : IAbility
{
public List<A> Abilities { get; protected set; }
}
public abstract class AbilityBase : IAbility
{
public abstract Category Category { get; }
public abstract int Value { get; }
public abstract string Description { get; }
}
Now the final classes are simple:
public class MentalAbilities : AbilitiesBase<MentalAbility> { }
public class MentalAbility : AbilityBase
{
public override Category Category => Category.Mental;
public override int Value => 42;
public override string Description => "Mental!";
}
There's nothing to implement in MentalAbilities
- the base class took care of that - but it does have a List<MentalAbility> Abilities { get; }
property.
MentalAbility
is also neatly set up to enforce you to override the properties.
Now, if you actually wanted still have a List<IAbility> Abilities { get; }
property then there's an easy way to do that by writing these two interfaces:
public interface IAbilities
{
List<IAbility> Abilities { get; }
}
public interface IAbilities<A> : IAbilities where A : IAbility
{
new List<A> Abilities { get; }
}
This forces a change to AbilitiesBase
like this:
public abstract class AbilitiesBase<A> : IAbilities<A> where A : IAbility
{
public List<A> Abilities { get; protected set; }
List<IAbility> IAbilities.Abilities => this.Abilities.Cast<IAbility>().ToList();
}
The List<IAbility> IAbilities.Abilities
property generates a copy of the public List<A> Abilities { get; protected set; }
property, but that's what you want to prevent anyone adding the wrong type of ability to the original list.
Personally, I would go with this instead:
public interface IAbilities
{
IEnumerable<IAbility> Abilities { get; }
}
public interface IAbilities<A> : IAbilities where A : IAbility
{
new List<A> Abilities { get; }
}
public abstract class AbilitiesBase<A> : IAbilities<A> where A : IAbility
{
public List<A> Abilities { get; protected set; }
IEnumerable<IAbility> IAbilities.Abilities => this.Abilities.Cast<IAbility>();
}