I'm looking for a way to create a generic base class that has a typesafe taxonomy using internal properties. Just to be clear, the class doesn't have to use the generics language feature as long as it is generic itself and I'm looking for something that has compile-time type safety.
As an example here is a simple taxonomy I want to represent using multiple instances of the same class
Wood
Crate
Box
Metal
Crate
Bar
The permutations of which are
Wood Crate
Wood Box
Metal Crate
Metal Bar
initially I though I could use enums to represent the different levels of taxonomy like so
public enum EFirstLevel
{
Wood,
Metal
}
public enum ESecondLevel
{
Crate,
Box,
Bar
}
public class BaseItem
{
EFirstLevel FirstLevel;
ESecondLevel SecondLevel;
public BaseItem(EFirstLevel aFirst, ESecondLevel aSecond)
{
FirstLevel = aFirst;
SecondLevel = aSecond;
}
}
I could create the items above using:
var item1 = new BaseItem(EFirstLevel.Wood,ESecondLevel.Crate)
var item2 = new BaseItem(EFirstLevel.Wood,ESecondLevel.Box)
var item3 = new BaseItem(EFirstLevel.Metal,ESecondLevel.Crate)
var item4 = new BaseItem(EFirstLevel.Metal,ESecondLevel.Bar)
but I could also create
var item5 = new BaseItem(EFirstLevel.Wood,ESecondLevel.Bar)
which for my purposes is incorrect.
Do any of you know of a pattern that would let me create a single class to represent the example taxonomy in a type-safe way that prohibits the creation of incorrect combinations.
It also needs to be applicable to N levels of taxonomy, the 2 levels above are just an example.
Thank you
let me know if you need any more info
Just to clairify i'm happy to have other supporting classes, what i'm trying to avoid is having to explicitly create a class for each taxanomic value.
What I'm trying to accomplish is providing an example layout of class that that maintains this taxanomic type safety so I can reflect over it and permute combinations. While maintaining the type safety should I need to generically instantiate said permutations.
The class upon which I might reflect could come form a third party and as such I might not know beforehand the values for each level. I could generate all the possible combinations into a set of classes with type safe internal enums but this would require regeneration of said classes any time you changed the items in any level. I was just wondering if there was a was to achieve my goals without having to generate any classes.
Think I've found what I'm looking for @Iridium had an answer close to what I think is going to be my solution, however rather than having to define each item as a class I think I've found a way to maintain the type safety and still be able to create the items as properties of a single base class.
As in @Iridium's answer it does require the creation of linked classes defining the taxonomic relationships.
Instead of using interfaces I remembered an SO answer I found a long time ago about pseudo enum inheritance with a protected constructor Question is Here see the answer by "Seven"
If I define 2 base classes on which I can base the taxonomic chaining 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;
}
}
I can then use these to chain as many levels deep as needed (for very deep trees this may not be ideal)
The first level inherits from the chain enum with no 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 chain link enum which defines a 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) { }
}
A third level would use a signature like
public class HEThirdLevelType : ChainLinkEnum<HEWoodItemTypes>
After that set-up I can then define my single base item class like:
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 wanted an item with 5 levels of taxonomy where each is linked to the one before
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 first level and 2 second levels both linked to 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 the single base class is defined, i can reflect over it and the chain enums to get the permutations.
each item is created as a property
var metalBox = new TwoLevelItem<HEBaseMaterial,HEMetalItemTypes>()
{
LevelOne = HEBaseMaterial.Metal,
LevelTwo = HEMetalItemTypes.Box
}
This maintains the type safety and means that I can new properties to a taxonomy level and not have to create classes for items(although I do have to generate the extra items as properties)
This seems to do all i wanted but i've yet to try it extensively.
@Iridium's answer was close but not quite what I was looking for, although it did help.