I think I found what I was looking for @Iridium received an answer close to what, in my opinion, would be my solution, but instead of defining each element as a class, I think I found a way to maintain type safety and still be able to create elements as properties of one base class.
As in @Iridium's answer, this requires the creation of related classes that define taxonomic relationships.
Instead of using interfaces, I remembered the answer of SO, which I found a long time ago on the inheritance of pseudo-exchange with a protected constructor. Question Here, see the answer "Seven"
If I define 2 base classes on which I can base taxonomic chain classes
public class ChainEnum { public int IntValue { get; protected set; } public static readonly ChainEnum None = new ChainEnum(1); protected ChainEnum(int internalValue) { this.IntValue = internalValue; } } public class ChainLinkEnum<TParent> : ChainEnum where TParent : ChainEnum { public TParent Parent { get; protected set; } protected ChainLinkEnum(int internalValue, TParent aParent) : base(internalValue) { Parent = aParent; } }
Then I can use them to chain as many levels as possible (for very deep trees this may not be ideal)
The first level inherits from enumerating a chain without a parent
public class HEBaseMaterial : ChainEnum { public static readonly HEBaseMaterial Wood = new HEBaseMaterial(1); public static readonly HEBaseMaterial Metal = new HEBaseMaterial(1); protected HEBaseMaterial(int internalValue) : base(internalValue) { } }
Subsequent levels inherit from the link chain enumeration that defines the parent
public class HEWoodItemTypes : ChainLinkEnum<HEBaseMaterial> { private static readonly HEBaseMaterial InternalParent = HEBaseMaterial.Wood; public static readonly HEWoodItemTypes Box = new HEWoodItemTypes(1); public static readonly HEWoodItemTypes Crate = new HEWoodItemTypes(1); protected HEWoodItemTypes(int internalValue) : base(internalValue, InternalParent) { } } public class HEMetalItemTypes : ChainLinkEnum<HEBaseMaterial> { private static readonly HEBaseMaterial InternalParent = HEBaseMaterial.Metal; public static readonly HEMetalItemTypes Box = new HEMetalItemTypes(1); public static readonly HEMetalItemTypes Bar = new HEMetalItemTypes(1); protected HEMetalItemTypes(int internalValue) : base(internalValue, InternalParent) { } }
The third level will use a signature like
public class HEThirdLevelType : ChainLinkEnum<HEWoodItemTypes>
After this setup, I can then define my only base element class, for example:
public class TwoLevelItem<T1,T2> where T1 : ChainEnum where T2 : ChainLinkEnum<T1> { public T1 LevelOne { get; set; } public T2 LevelTwo { get; set; } }
or if I need an element with 5 levels of taxonomy, where each one is associated with it until
public class FiveLevelItem<T1,T2> where T1 : ChainEnum where T2 : ChainLinkEnum<T1> where T3 : ChainLinkEnum<T2> where T4 : ChainLinkEnum<T3> where T5 : ChainLinkEnum<T4> { public T1 LevelOne { get; set; } public T2 LevelTwo { get; set; } public T3 LevelThree { get; set; } public T4 LevelFour { get; set; } public T5 LevelFive { get; set; } }
or 3 properties with one level of the first and second levels that are associated with the first
public class LinkedItem<T1,T2_1,T2_2> where T1 : ChainEnum where T2_1 : ChainLinkEnum<T1> where T2_2 : ChainLinkEnum<T1> { public T1 LevelOne { get; set; } public T2_1 LevelTwoOne { get; set; } public T2_2 LevelTwoTwo { get; set; } }
Once one base class is defined, I can flip it, and the chain enumerates to get permutations.
each item is created as a property
var metalBox = new TwoLevelItem<HEBaseMaterial,HEMetalItemTypes>() { LevelOne = HEBaseMaterial.Metal, LevelTwo = HEMetalItemTypes.Box }
This maintains type safety and means that I can add new properties to the taxonomy level and not create classes for elements (although I need to create additional elements as properties)
It seems that I am doing everything I want, but I have not tried to do it yet.
@Iridium's answer was close, but not quite what I was looking for, although it really helped.