Introduction

In this article, I want to show you an approach of how a structured enumeration can be handled by C#.

Background

While playing with my little home project, I stumbled upon a problem of having all those category enumerations displayed in a tree. I wanted to keep it simple - as the enumerations are - while avoiding the need to create structured object hierarchies for every one of them. So, after a bit of thinking, I came up with this solution. I hope you'll find it useful or at least interesting.

Structured Enumeration

First, we have to tickle our old plain list enumeration a bit and convert it to a structured one. I chose the animal categories.. well.. to confess, I'm always having slight difficulties to find a good example, but here it is anyway.

This simple attribute is composed of only one property (an automatic property may be used in C# 3.0, God I love those). The attribute is responsible for determining the span (multiplicator) of the tree levels thus allowing us to distinguish the hierarchy later.

Well.. later is now because this ought to be a short article. I took the liberty to create the utility class to help us deal with the structured enumerations. It consists of two staticmethods. Let's take a closer look at them.

Methods

The IsChild method is used to determine if one enumeration value is placed under another one. I guess the utility can be easily extended by a method determining the whole chain of the parents from a particular enumvalue.

Another useful method that may be of interest to us is the CreateList method. It obviously creates a list of the child enumeration values under a particular parental value. It will also allow us to use the output list for the display or various cycle purposes. I can imagine an iterator here.

Possible Enhancements

As I said earlier in this article, I can imagine some kind of iterator (or perhaps an indexer) instead of the CreateList method.

The default indexing capabilities of enumerations can be widened by "inheriting" the enumeration from ulong type instead of default uint.

The utility can also be extended with any kind of structuring routine which suits your need such as retrieving the chain of parents for a particular value.

Limitations

The complex trees with many levels may find their limit because the indexing will reach the limit of enumeration (ulong). This limitation can be reduced by lowering the span value on the attribute, thus allowing to scale for the count of branches against the count of levels.

It is recommended to use some default (zero) value which will then be used to retrieve the level one branches.

Personal Note

I found these structured enumerations quite useful myself dealing with countless - now waiting to be structured - category enumerations. They cut the time needed to create the editable trees where the categories are distinguished from the instance items. Moreover, the enumeration is still one type in the end.

Good luck and I will be pleased to hear your comments.

History

2008-04-02: Missing example was added (Shall I ever get it right the first time?)

You're still hitting Reflection to get the Attributes on each call.
This is very costly and unnecessary.
They don't change, the values don't change; read them once and cache them, then only hit the cache.

Regardless of the active discussion of the technique (I like the overall idea a lot, personally), it took me a careful reading to "get" the meaning of the 100 value in the attribute.

A line or two of explanation might be a good addition. Maybe:
Enums with the Structured attribute can have their numeric values interpreted to get their parent. Some number of elements are reserved for possible children (the parameter to the Structured attribute) and numbers above that represent the parent.

For example, if the size is 10, the 3rd element is still 3, but its children are 3*10+0 to 3*10+9. The children of the second child of 3 is

(3*10+1)*10+0 to (3*10+1)*10+9
\---------/
|
Parent shifted left by the size to reserve

Hi, good ideas, typically I'm just emitting thoughts I don't care much about the final implementation . I let the reader decide which implementation suits him. Mostly I don't have time to polish them to the extreme knowing that is a waste of my time. Because no matter how sofisticated you'll make it somebody will complain or suggest. On the other hand if I'll post almost the raw version the readers will have the pleasure to optimize it themselves.

Too extreme ? My point is that I leave to a programmer to implement it the way he likes. I cannot and don't want to suggest all the possibilities. If you prefer ChildrenOf over CreateList I can not stop you. I just want to share the ideas I stumbled upon and let them inspire the readers to improve them, adapt them or completely dump them. :P

Where if ttype used?: There's no constraint for Enum (it is not possible by C# design) moreover the Attribute is enum only so it ensures that TType will be Enum because otherwise the GetCustomAttributes will be zero.

This is needless: This is my coding pattern of choice (substition pattern) it is IMO more readable then putting altogether (cumulative pattern). Consider this :

I would leave them in place (at the high-end), leave low-order fields as 0 to indicate "class", and add (OR) detail as I go.
The "class" parts could also be at the low-end, but I think high-end is clearer.
(Hexadecimal literals would make it easier too. They following doesn't work correctly, but should give a decent conceptual view.)

Sorry, I got confused with another thread.. I guess I'm getting tired.. It's 23:37 here. I'm going to sleep. I checked your solution and this is ofcourse possible but consider the scenario where the Span (as I call it) won't be 0xFF but something lower than that (for example 0xC) because the complexity of the tree won't be sufficient to hold the number anymore.

IMHO.. in the end it is not important if you choose decimals, hexadecimals or any other representation of the value. I guess it's up to reader to make it suitable. I just thought that using decimals numbers as an example will be understable to a more general public.

And, no, I haven't worked that out yet either, but I expect yout Attribute-based solution will be the way to go.

I also acknowledge that you were after a solution that wasn't tied to a particular enum.

Exactly, prior to posting the article (or even thinking about the solution itself) I was considering all the ways possible to achieve that with either standard enumerations or any other easy way. It was expected that the consensus that this solution is not reinventing the wheel (as leppie pointed out) is first to be made.

The Structured attribute is there only to normalize all the possible granulations of enumerations so then one method can be used to reconstruct the tree + some value is ofcourse in the methods themselves.

We'll get there, baby steps.

Sure and the reasonable compromise of syntax is what I'm after too. I'm glad for every suggestion and I guess I'm willing to modify the article when the best solution is worked out.

There are some advantages inyour solution, but for me not enough to warrant the unusual solution. It's pretty limited anyway (whether you use binary masking or some decimal grouping), binary is much more common and thus preferable in my book.

We are a big screwed up dysfunctional psychotic happy family - some more screwed up, others more happy, but everybody's psychotic joint venture definition of CPblog: TDD - the Aha! | Linkify!| FoldWithUs! | sighist

This example - as you pointed out in comments - uses a span (or granularity.. used by PIEBALDconsult) of 16 thus allowing room for 15 items. Sure if you need to make another enumeration and you want to have more than 16 items you'll make span wider for example.

Now try to use ONE method to reconstruct the tree for both enumerations. Not possible ?

Whether you'll choose hexadecimals over decimals or high-ordering over low-ordering is not a concern of this article as much. Rather it is a normalized way to reconstruct a tree from the enumerations with different granularities.

Whether you'll choose hexadecimals over decimals or high-ordering over low-ordering is not a concern of this article as much. Rather it is a normalized way to reconstruct a tree from the enumerations with different granularities.

Yes, but it should also be fairly efficient and maintainable, and I suspect that having the class (or whatever) value shift around is the opposite of that.

Smart K8 wrote:

Now try to use ONE method to reconstruct the tree for both enumerations.

Now that it's the weekend I have more time to think about this. So what form should the tree take?

Whether you'll choose hexadecimals over decimals or high-ordering over low-ordering is not a concern of this article as much. Rather it is a normalized way to reconstruct a tree from the enumerations with different granularities.

Yes, but it should also be fairly efficient and maintainable, and I suspect that having the class (or whatever) value shift around is the opposite of that.

You surely are right on the point of efficiency and maintainability. I was just answering to leppie who didn't get that this idea is not a reinventing the wheel and it is not possible to structure the tree using the bitwise masks this will only get you all the parents but not an immediate one.

Smart K8 wrote:

Now try to use ONE method to reconstruct the tree for both enumerations.

Now that it's the weekend I have more time to think about this. So what form should the tree take?

I guess before we address that; what functions should be available?

Basically you should have the EnumType.IsChild(EnumType parent) which will detect if a specified enumeration value is a child of a specific direct parent (I'll try to elaborate on the Extension methods in C# if there is a way to extend the enumerations via System.Enum but I guess it is not possible). The listing method is only the cream on the top of it. With the IsChild (or an alternative method) you should be able to reconstruct a tree (either visual one or non-visual one). That's the main point of the article. If there's better way of achieving that with enumerations in different hierarchy style (either low-ordering) it will be an improvement (and I'll be happy to hear about it).