24699

Is there an alternative to the Curiously Recurring Template Pattern?

Question:

I've caused myself some headaches over the past couple of weeks with the <a href="http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern" rel="nofollow">curiously recurring template</a> pattern.

Following on from these two questions of mine:

<ul><li><a href="https://stackoverflow.com/questions/3377597/whats-the-correct-way-of-retrieving-my-custom-enumeration-classes-by-their-value" rel="nofollow">What’s the correct way of retrieving my custom enumeration classes by their value?</a></li> <li><a href="https://stackoverflow.com/questions/3449363/why-are-my-static-objects-not-being-instantiated-when-first-access-to-the-static" rel="nofollow">Why are my static objects not being instantiated when first access to the static class is a static method on the base class?</a></li> </ul>

How can I improve the following example:

public class DocketType : Enumeration<DocketType, int, string> { public static DocketType Withdrawal = new DocketType(2, "Withdrawal"); public static DocketType Installation = new DocketType(3, "Installation"); private DocketType(int docketTypeId, string description) : base(docketTypeId, description) { } }

I want a static method that I don't have to repeat in the Enumeration class:

public abstract class Enumeration<TEnum, X, Y> : IComparable where TEnum : Enumeration<TEnum, X, Y> { protected Enumeration(X value, Y displayName) { AddToStaticCache(this); } public static TEnum Resolve(X value) { return Cache[value] as TEnum; } }

The problem with this, as you'll see from my second linked question, is that the call to Enumeration<DocketType, int, string>.Resolve(X value); does not cause the DocketType static objects to be instantiated.

I'm not adverse to totally rewriting this from scratch. I'm aware it's a big code smell. Currently, to get this working my base class has the protected static method ChildResolve and I've added Resolve to each of my Enumeration classes. Nasty stuff!

<strong>ANSWER:</strong>

Seems there was no nice alternative to the pattern, so I stuck with the pattern and took inspiration from the accepted answer and came up with this:

static Enumeration() { GetAll(); } public static void GetAll() { var type = typeof(TEnum); var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); foreach (var info in fields) { var locatedValue = info.GetValue(null) as Enumeration<TEnum, X, Y>; Cache.Add(locatedValue.Value, locatedValue); } }

This is also the same code using in the CodeCampServer MVC example project, so I feel less dirty for using it!

Answer1:

It's not very elegant, but something like this might do the trick:

public class DocketType : Enumeration<DocketType, int, string> { public static readonly DocketType Withdrawal = new DocketType(2, "Withdrawal"); public static readonly DocketType Installation = new DocketType(3, "Installation"); private DocketType(int docketTypeId, string description) : base(docketTypeId, description) { } } public abstract class Enumeration<TEnum, TId, TDescription> : IComparable where TEnum : Enumeration<TEnum, TId, TDescription> { private static readonly Dictionary<TId, TEnum> _cache; static Enumeration() { Type t = typeof(TEnum); _cache = t.GetFields(BindingFlags.Public | BindingFlags.Static) .Where(f => f.FieldType == t) .Select(f => (TEnum)f.GetValue(null)) .ToDictionary(e => e.Id, e => e); } public static TEnum Resolve(TId id) { return _cache[id]; } public TId Id { get; private set; } public TDescription Description { get; private set; } protected Enumeration(TId id, TDescription description) { Id = id; Description = description; } // IComparable public int CompareTo(object obj) { // TODO throw new NotImplementedException(); } }

Answer2:

You need to push your static fields into a class that has a static instance as instanced fields. That way you access your enumeration through a single static member, which immediately instances all the enumeration members.

A quickly thrown together example:

// The Collection of values to be enumerated public class DocketEnum : EnumarationCollection<DocketType, int, string> { // Values are fields on a statically instanced version of this class public DocketType Withdrawal = new DocketType(2, "Withdrawal"); public DocketType Installation = new DocketType(3, "Installation"); // The publicly accessible static enumeration public static DocketEnum Values = new DocketEnum(); } // The actual value class public class DocketType : EnumerationValue<DocketType, int, string> { // Call through to the helper base constructor public DocketType(int docketTypeId, string description) : base(docketTypeId, description) { } } // Base class for the enumeration public abstract class EnumarationCollection<TType, X, Y> where TType : EnumerationValue<TType, X, Y> { // Resolve looks at the static Dictionary in the base helpers class public TType Resolve(X value) { return Cache[value] as TType; } public static Dictionary<X, EnumerationValue<TType, X, Y> > Cache = new Dictionary<X, EnumerationValue<TType, X, Y>>(); } // Base class for the value public abstract class EnumerationValue<TType, X, Y> where TType : EnumerationValue<TType, X, Y> { // helper constructer talks directly the the base helper class for the Enumeration protected EnumerationValue(X value, Y displayName) { EnumarationCollection<TType, X,Y >.Cache.Add(value, this as TType); } } class MainClass { public static void Main (string[] args) { // You can immediately resolve to the enumeration Console.WriteLine(DocketEnum.Values.Resolve(2).ToString()); } }

Answer3:

You want to do something "to all subclasses of a given type". Anything of that nature is impossible without using AppDomain.Current.GetAssemblies() and iterating through them. If you take this approach, you can optimize the performance by creating an assembly-level attribute that you only apply to your assemblies, (and others that should be included in your subclass search.) and use that when preparing to invoke .GetTypes() on each assembly.

To be clear, here's an example of what getting all those subclasses might look like:

Type[] subclasses = AppDomain.CurrentDomain.GetAssemblies() .Where(x => Attribute.IsDefined(typeof(MyEnumeartionAssemblyAttribute))) .SelectMany(x => x.GetTypes()) .Where(x => x.BaseType != null && x.BaseType.IsGenericType && x.BaseType.GetGenericTypeDefinition() == typeof(Enumeration<,,>));

From there it should be a simple matter of using reflection on each System.Type and doing what you will with the static fields.

Answer4:

If you do want to force another class's static constructor to run, you can use <a href="http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.runclassconstructor.aspx" rel="nofollow">RuntimeHelpers.RunClassConstructor</a>. You can call it from the static constructor of Enumeration<TEnum, X, Y> so that it will be run the first time you use a static method on any instantiation of the generic type:

static Enumeration() { RuntimeHelpers.RunClassConstructor(typeof(TEnum).TypeHandle); }

Recommend

  • Unable to populate Kendo Scheduler with Data
  • Using a complex .d.ts file (chrome-app.d.ts)
  • Working with a severely limited interpreted language
  • Java Template library similar to ZPT (attribute language)
  • Mongoose - can't access object properties?
  • TypeError: api.getAll is not a function, service method is not recognized
  • Ruby: Alter class static method in a code block
  • Is it OK to write code after [super dealloc]?
  • Unknown C# type
  • What is the first step to using a REST API in Rails?
  • Insert space after period using sed
  • How to get the index of element in the List in c#
  • command line of process by name
  • Zend Framework bassed projects
  • Python PIL to extract number from image
  • ZipList with Scalaz
  • Primefaces lazy datascroller calling load twice
  • Android device acting as an accessory
  • Possible to “watch” both HAML and SASS at the same time?
  • How to specify input and output paths from cmd.exe for a PowerShell script?
  • Implicit joins and Where in Doctrine - how?
  • I18n locale disregarding fallbacks
  • converting text file into xml using php?
  • How do I superscript characters in a UIButton?
  • Alternative To body {overflow:scroll;} That Will Prevent Page Jostling/Wriggling?
  • How to define and use opencv mat of user type
  • Setting up SourceTree to merge unity3d scenes with UnityYAMLMerge
  • What is Eclipse's Declaration View used for?
  • Cassandra Data Model
  • Jquery - Jquery Wysiwyg return html as a string
  • Arrays break string types in Julia
  • WPF Applying a trigger on binding failure
  • Getting Messege Twice Using IMvxMessenger
  • Java static initializers and reflection
  • Linking SubReports Without LinkChild/LinkMaster
  • EntityFramework adding new object to nested object collection
  • Checking variable from a different class in C#
  • failed to connect to specific WiFi in android programmatically
  • Python/Django TangoWithDjango Models and Databases
  • How can I use threading to 'tick' a timer to be accessed by other threads?