General class with explicit type taxonomy

I am looking for a way to create a base base class that has a type taxonomy using internal properties. To be clear, a class should not use a generics function as long as it is generic, and I'm looking for something that has compilation type security.

An example is a simple taxonomy that I want to introduce using multiple instances of the same class

Wood Crate Box Metal Crate Bar 

Whose permutations

 Wood Crate Wood Box Metal Crate Metal Bar 

I originally, although I could use enumerations to represent different levels of taxonomy, for example,

 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 elements 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 is wrong for my purposes.

Do any of you know of a template that would allow me to create one class to represent the taxonomy of the example as a safe type that prohibits the creation of incorrect combinations.

It should also be applicable to N levels of taxonomy, 2 levels above is just an example.

Thanks.


Update: I require compilation type security. I could do this with a few classes, easily using inheritance, etc. I am trying to find a solution using instances of only one base class.

let me know if you need more information


Update: @Maarten Yes, I try to maintain the hierarchy so that if EFirstLevel is 1, then ESecondLevel should be either Crate or Box.

Just to make clair, I'm glad that I have other supporting classes, which I am trying to avoid is to explicitly create a class for each taxonomic value.

What I'm trying to accomplish is to provide an example of a class layout that supports this taxonomic type security so that I can reflect on it and rearrange the combinations. While maintaining type safety, I need to outline the instances of these permutations.

A class in which I could think could arise from a third party, and so I might not know in advance the values ​​for each level. I could generate all possible combinations into a set of classes with internal enumerations of type safe, but this would require the restoration of the specified classes each time the elements are changed at any level. I just wondered if there was a goal to achieve my goals without creating any classes.


EDIT: moved this section in response
+4
source share
4 answers

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.

0
source

I don’t think you can leave without creating classes / interfaces and having no compile-time checking to make these objects fit your taxonomy.

I propose a solution as follows:

 // Define the taxonomic levels here. Each level (except the first) references its next-higher taxonomic level in a type constraint interface Material { } interface Object<TMaterial> where TMaterial : Material { } // Define the items in the highest taxonomic level (materials) interface Wood : Material { } interface Metal : Material { } // Define the items in the 2nd taxonomic level (objects), implementing the appropriate interfaces to specify what the valid top-level taxonomies it can fall under. interface Crate : Object<Wood>, Object<Metal> { } interface Bar : Object<Metal> { } interface Box : Object<Wood> { } // Define an item class with type constraints to ensure the taxonomy is correct abstract class Item<TMaterial, TObject> where TMaterial : Material where TObject : Object<TMaterial> { } 

With the above definition, we can now define the actual elements:

 class MetalBar : Item<Metal, Bar> { } class MetalCrate : Item<Metal, Crate> { } class WoodCrate : Item<Wood, Crate> { } class WoodBox : Item<Wood, Box> { } 

However, an attempt to create an invalid element (for example, a wooden bar) results in a compile-time error:

 class WoodBar : Item<Wood, Bar> { } 

The type "Taxonomy.Bar" cannot be used as a parameter of type "TObject" in the generic type or method "Taxonomy.Item". There is no implicit link conversion from Taxonomy .Bar to Taxonomy.Object

+3
source

Two ways -

  • Create an enumeration that will contain the leaves of your tree, in your case Wood_Crate, Wood_box and so on. Easy to operate and less easy to maintain or read.

  • Define a Category class and a static class for each of the elements in your tree. for instance

     public class Category { internal Category() {}; public string Id; // This would be used to understand what you got, can be a list of enums or something } public static Category CrateCategory = new Category {Id = "Wood.Crate"}; public static class Wood { // either way will work, one will let you directly access CrateCategory (if that is what you wish), the second will remove the need for different Crate categories public static Category Crate { get { return CrateCategory; } } public static Category Box { get { return new Category { Id = "Wood.Box" }; } } } 

The BaseItem constructor will only get a category. And could write something like

 new BaseItem(Box.Crate); 

If you put the Category class in a different assembly, you will see that no one can create their own categories.

This is a bit more work, but seems more elegant and readable to me. If N is extremely large, you can write code to create classes and category identifiers for you.

+1
source

My suggestion was to create a class for each type, and then allow a valid subtype to inherit from that type.

Finally, change the BaseItem type to generic and only valid types are allowed.

Like it (Sorry, poorly explaining)

 class Wood {} class Metal {} class Crate : Wood, Metal {} class Box : Wood {} class Bar : Metal {} class BaseItem<T1, T2> where T2 : T1 { } 

This will give you the opportunity to compile a time type (but I don't think this is the best way)

0
source

Source: https://habr.com/ru/post/1496956/


All Articles