Wednesday, February 25, 2009

Tom’s Patterns Cheat Sheet, Part 4 – Façade and Bridge Patterns

For this post I'm reverting to the original patterns Bible as my primary source, the GoF Design Patterns tome.

Façade

The Façade Pattern

Provides a simple interface to a complex subsystem. Helps decouple clients from the individual elements of a subsystem. Useful for enforcing layering of subsystems. Web service interfaces to legacy systems are good examples of the façade pattern:

Web Services Example of The Façade Pattern

Bridge Pattern

The Bridge Pattern

"Decouples an abstraction from an implementation." In other words, allows you to create a separate class hierarchy for the object model that the client cares about (the "abstraction") and for the object model that internally implements its functionality.

A trivial example might be a Customers class that internally uses an ADO recordset for data access:

Trivial application of the Bridge Pattern

This is a trivial case because the implementer can't be subclassed, but it could still be useful for cases where you want to be able to inject ADO recordsets with different data sources into your Customers object, without having to change the Customers class itself.

We can make this example slightly more sophisticated by replacing the recordset with a generic data access class that could be specialized to use ADO, XML, flat files or whatever:

Application of the Bridge Pattern

A good code sample of this type of application of the bridge pattern can be found here.

This is useful when

  • You would like to reuse the implementer outside the context of the abstraction. A recordset implementer is a good example of this.
  • You need the ability to swap out implementations at runtime. A recordset that can be configured to use multiple data sources is an example. Machine- or OS-specific implementations are another example, e.g. an XP v. Vista implementations.
  • You want to simplify the class hierarchies and/or provide greater extensibility flexibility by splitting up the system into separate class hierarchies for the abstraction and its implementation.

Wednesday, February 18, 2009

Tom’s Patterns Cheat Sheet, Part 3 – Model View Presenter

Considered a descendant of Model View Controller, MVP comes in two main forms, which patterns god Martin Fowler calls Supervising Controller and Passive View. A significant variant of Passive View is the Humble View, which is epitomized by Michael Feathers' classic article The Humble Dialog Box, and a very significant variation of that is Presenter First.

Most discussions of MVP begin with MVC and proceed in chronological order, but I find this ordering illogical. From a practical standpoint, the primary reason to choose MVP is to increase testability of your UIs, so the logical starting point is the Humble View form of Passive View.

Passive View MVP

Passive View MVP

Dotted lines represent communication by events while solid lines are direct object messages. The roles of the components are as follows.

View: In this architecture the view is kept as "humble" (that is, as free of code and logic) as possible. UI technology is often difficult to unit test (though as Fowler says, this point is often over-stated) so the goal of Humble View is to remove anything from the view that might be worth testing. The view will generally not be covered by unit tests at all in this architecture.

Presenter: The view simply passes notification of user stimulus to the presenter, whose role is similar to a controller in MVC. The presenter accesses the view only through a UI-agnostic interface so that it can be easily mocked in unit tests. The presenter updates the model appropriately and can receive events from the model when its state changes. The presenter is entirely responsible for updating the view; the view has no direct knowledge of or interaction with the model. The reason for this is to simplify the logic and make the code more readable and maintainable by putting all UI manipulation code in a single place.

A presenter differs from a controller primarily in its granularity: presenters are created at the form/page/complex widget level, and not at the widget/window level as are controllers. Also in Passive View MVP presenters are responsible for updating the view as well as for responding to user interaction, while controllers are only responsible for the latter.

Model: As with MVC, the model has no knowledge of the presenter or the view.

Some other variations on Passive View:

Passive View MVP with a Presenter-Model Observer

By removing the View's direct knowledge of the presenter you can decouple the view from the presenter and allow the same view to be used with different presenters. There is however a penalty to be paid in loss of readability whenever the Observer pattern is used, and this can sometimes be significant.

Passive View MVP with a Model Interface

By adding an interface for the model you can improve testability of the presenter yet again. This can be used with or without the Presenter-View Observer. In fact I would encourage use of a model interface for all but the most trivial of models. At this point we've arrived at the MVP variant used by Presenter First, though Presenter First is really a variation of TDD, and so is as much a design/development process as an architecture.

It should be noted that Fowler considers the View interface optional, but from a practical standpoint we can assume its existence.

Supervising Controller

Historically, Supervising Controller was developed before Passive View, but logically it's the pattern you revert to in order to take advantage of data binding:

Supervising Controller MVP

With this pattern you would allow data binding of the model to the view to handle the "easy" synchronization work, and would reserve the presenter for handling any complex logic. Although this is structurally more complex than Passive View, in practice where data binding is strong it can result in far simpler code. The Presenter-Model Observer has been removed from the diagram above, but there's no reason it couldn't be added back if that were appropriate for the situation.

For additional variations on MVP, I found this to be an interesting post. I will reserve creation of MVP code samples for a future post.



Tom’s Patterns Cheat Sheet, Part 2 – Model View Controller

MVC is one of the oldest, most widely known architectural patterns. It involves decomposition of the system into three parts, using terminology from patterns god Martin Fowler's GUI Architectures paper:

  • Model: domain object model.
  • View: user interface that displays the state of the model.
  • Controller: an object that receives user input and updates the model, and possibly the view.

Here's the usual MVC dependency diagram:

This means the model is independent of (has no knowledge of) the views and the controllers, the view and the controller have strong knowledge of the model, and the controller has knowledge of the view. So we would expect, for example, to be able to make drastic changes in the view without affecting the model at all, but changes in the model will probably mandate changes in the view.

MVC has two primary uses: rich client GUIs and thin client Web applications. It was originally developed for rich client UIs, but in that space it has mostly been superseded by other patterns. But it's alive and well in the web space, and is the basis of both Ruby on Rails and ASP.NET MVC. See this MSDN article for a good overview.

MVC in Rich client GUIs: mainly uses a variant called active model. "Active" because the model takes action (notifies an observer, raises an event) that lets views and controllers know when its state has changed. The general data flow goes like this:

  • User stimulus happens, controller receives the event (often without the view's involvement at all)
  • Controller updates the model
  • Model raises a data changed event
  • View traps the event and updates itself

Graphically this looks as follows. A good example of this can be found here.

Dotted lines are used to indicate communication via events, solid lines to indicate direct manipulation. Some notes:

  • There may be multiple views and multiple controllers working off a single model.
  • The controller and the view don't depend on one another in this picture. Martin Fowler actually leaves the controller -> view dependency off his dependency chart because, he says, it's not often used in the rich client GUI case. It is used commonly in the web case however.
  • This is often implemented at a very low level of granularity – one MV pair for each widget, or even for each window in each widget.
    • In that case the "model" may represent a value stored in a widget, and not in a database.

Pros:

  • Separation of model from view. Allows changes in the view without affecting the model. Good for cases where the views change more frequently than the model, as is usually the case.

Cons:

  • Use of observers/events increases complexity.
  • Frequent updates to the model could result in many data changed events and suboptimal performance.
  • Separates view from model writes, but not from model reads. Since views are difficult to unit test, this means logic involved in transforming model data for presentation is not covered.

MVC in Web applications:

Uses a variant called passive model. "Passive" because the model makes no effort to inform anyone that its state has changed. Flow is as follows:

  • HTTP request happens, controller on the server receives the request.
  • Controller updates the model, usually resulting in a database update.
  • Controller selects the view appropriate for the request, provides it with any model data it needs, and returns it, typically in the form of HTML, XML or JavaScript.
  • The browser renders the view.

This looks like any well factored web application that you've ever seen, in which the domain model is carefully separated from the web UI code, but there is an important difference. In MVC web applications the web UI code is separated logically and physically into separate view and controller elements. Some notes:

  • The view is as simple as possible, usually just an HTML template containing some simple server-side code snippets.
  • The controller is designed for maximum testability. In the case of ASP.NET MVC, it is free of UI and web server dependencies, which the ASP.NET MVC framework provides to the controller via easily mocked interfaces. In the case of Rails, controllers are designed to be easily testable using functional tests.
  • The view and controller are larger grained than in the rich-client case, generally at the level of one controller/view per page.

Pros:

  • Separation of controller from model helps manage complexity of large web applications.
  • Designed to support Test Driven Development (TDD).

Cons:

  • For small to medium sized web applications, increases complexity.


Monday, February 16, 2009

Tom’s Patterns Cheat Sheet for .NET – Part 1: Template Method, Strategy, Observer, Adapter, Proxy Patterns

This post started as a brownbag presentation I put together last year on Russ Olsen's Design Patterns in Ruby, so he deserves attribution. Parts also are based on Jeffrey Richter's classic Applied Microsoft® .NET Framework Programming.

Template method

The template method implements an algorithm that makes use of the virtual or abstract methods, which subclasses can specialize.


public abstract class AbstractClass
{
public void TemplateMethod()
{
// A real template method example would implement a more complex algorithm
Operation1();
Operation2();
Operation3();
Operation4();
}
// These operations may be abstract or virtual
protected abstract void Operation1();
protected abstract void Operation2();
protected abstract void Operation3();
protected abstract void Operation4();
}

This is useful when the outline of an algorithm is fixed but the specific implementations of pieces of the algorithm may change. E.g. a class that encrypts and compresses data might delegate the specific encryption and compression algorithms to use to subclasses.


Strategy Pattern

Traditional strategy pattern diagram:

You might think of this as "algorithm injection." The "strategy" object is just a wrapper for a method. Clients "inject" the method they want to use by implementing their own strategy object and injecting it into the context. Similar to the template method pattern, but the client in this case doesn't have to subclass the class containing the template method.

In these days of anonymous methods, lambda methods, and code blocks the traditional strategy pattern diagram looks like a circumlocution. Nowadays we would rather visualize passing method contents around directly, without the need for an object wrapper, even if the compiler is really providing the object behind the scenes. I'm not really sure how to draw it in UML, but it would look something like this:

where the code blocks are the strategy operation method contents. A simple example of the strategy pattern:


List<string> animals = new List<string> { "lion", "tiger", "giraffe", "elephant", "fox", "dog", "bear" };
animals.Sort(new Comparison<string>((x, y) => x.Length.CompareTo(y.Length)));

List<string> is the context. Comparison<string> is just a delegate; that's the strategy. (x, y) => x.Length.CompareTo(y.Length) is a lambda method; that's the strategy operation method contents.


Observer Pattern:

The object being observed keeps a list of objects that are observing it, and calls a notification method in each observer whenever its state changes. Objects register themselves to observe a subject by calling its AddObserver method, and unregister themselves by calling the RemoveObserver method. Simple implementation of the observer pattern:


abstract class Observer
{
public abstract void NotifyChanged(Subject subject);
}

class Subject
{
public List<Observer> Observers = new List<Observer>();

bool _foo = false;


private void NotifyObservers()
{
foreach (Observer observer in Observers)
observer.NotifyChanged(this);
}

public bool Foo
{
set {
_foo = value;
NotifyObservers();
}
}
}

For the sake of brevity I'm publicly exposing the List<Observer> observers field. Its Add and Remove methods will act as the AddObserver and RemoveObserver methods. When the state of the subject changes, it iterates the observer list, calling their NotifyChanged methods.

The above code isn't very practical in C# due to its lack of support for multiple inheritance. Any observer would burn its one and only base class just so it could listen to the subject's notifications. Instead, we would want to create a delegate for the NotifyChanged method, which the observers could use to have the subject call back to them when the subject changes.

I have a good friend whose favorite interview question for .NET engineers is, "How do you implement the observer pattern in .NET?" It stumps most candidates because .NET provides such deep native support for the pattern that we tend not to think about it:


class Subject
{
event EventHandler Changed;

bool _foo = false;

private void OnChanged(EventArgs e)
{
Changed(this, e);
}

public bool Foo
{
set {
_foo = value;
OnChanged(EventArgs.Empty);
}
}
}

The "event" keyword hydrates into a lot of observer goodness. First, it causes the compiler to auto-generate the delegate for the Changed method that I said we would want above. Remember that a delegate is just a class. This takes the place of the abstract Observer class in the first code listing. Second, it causes a delegate field to be added to the Subject class. This takes the place of the Observers field in the first code listing. And finally, it causes Add and Remove methods to be added to the Subject class.

.NET delegates come with built-in support for "delegate chaining," or managing lists of delegates. So the auto-generated delegate field can already handle lists observers, and the Changed(this, e) call automatically iterates the list.


Adapter Pattern

The goal of the pattern is to wrap the "adaptee" in a class that matches the interface expected by the client. It's useful when you have an existing class that provides the functionality needed by the client, but using a different interface than the one it expects. Here's a simple code sample of the adapter pattern:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Adapter
{
class Client
{
private Target _target;

public Client(Target target) {
_target = target;
}

public void Foo() {
_target.OperationA();
}
}

class Target
{
public virtual void OperationA() { }
}

class Adaptee
{
public void OperationB() {

Console.WriteLine("Performed OperationB");
}
}

class Adapter : Target
{
private Adaptee _adaptee;


public Adapter(Adaptee adaptee) {
_adaptee = adaptee;
}

public override void OperationA() {
_adaptee.OperationB();
}
}

class Program
{
static void Main(string[] args)
{
Client client = new Client(new Adapter(new Adaptee()));

client.Foo();
Console.ReadLine();
}
}
}


Proxy Pattern:

The proxy pattern is used for inserting a class between clients and the real service that they are connecting to. Common types of proxies include protection proxies, which perform an access check before allowing the client to call the service, and remote proxies, which encapsulate the complexities of making method calls on a service in another process or on another machine.

Proxies are everywhere. If we create a console application in Visual Studio, and add a service reference to a default WCF service, Visual Studio auto-generates one for us:


Service1Client is a client proxy that implements the IService1 interface. "GetData" is the operation implemented by the service. In this case the proxy doesn't match the traditional diagram exactly, because the proxy doesn't hold anything we could call a reference to the real service. But the point of that reference in the traditional diagram is to convey that the proxy has the ability to pass calls through to the real service, and our WCF proxy does exactly that.

Monday, February 9, 2009

Best YAGNI quote ever

I just wish I could say it was mine. This gem is from Russ Olsen's Design Patterns in Ruby:

Look at it this way: Barring a sharp blow to the head, as you stand here today you are as dumb as you ever will be. We are all learning, getting smarter every day. This is especially true in software projects. You can be sure that you will have a better grasp of the requirements, technology, and design of any software system that you work on at the end of the project than at the beginning. Whenever you put in a feature before you really need it, you are guilty of programming while stupid; if you wait until you really need the thing, you are likely to have a better understanding of what you need to do and how you should go about doing it.

Friday, February 6, 2009

Fast enumeration to string conversion in C#

Code

.NET's ToString() behavior on enumeration values is nice, translating the value to the string representation of it that we see in code, as this simple console application shows:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace FastEnumToString

{

    class Program

    {

        public enum Color

        {

            Red,

            White,

            Blue,

            Green

        }

        static void Main(string[] args)

        {

            Console.WriteLine(Color.Red);

            Console.WriteLine(Color.White);

            Console.WriteLine(Color.Blue);

            Console.WriteLine(Color.Green);

            Console.ReadLine();

        }

    }

}

which yields this output:

Red

White

Blue

Green

But Microsoft has this warning about Enum.ToString:

Because this method searches metadata tables, it heavily uses system resources and can impede performance.

.NET uses reflection to look up the string representation of the enumeration when .ToString() is called. This simple test application shows the performance of the call on my laptop:

using System;

namespace FastEnumToString

{

    class Program

    {

        public enum Color

        {

            Red,

            White,

            Blue,

            Green

        }

        static void Main(string[] args)

        {

            var start = DateTime.Now;

            var rand = new Random();

            for (int i = 1; i < 1000000; i++)

            {

                var color = (Color)rand.Next(4);

                string str = color.ToString();

            }

            TimeSpan span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);

            Console.WriteLine("ToString() took " + span.ToString());

            Console.ReadLine();

        }

    }

}

Which yields:

ToString() took 00:00:02.7460000

So it took 2.746 seconds to convert an enum to a string a million times. This may not seem too bad, but in a recent profiling of the application I work on we found that calling Enum.ToString() was one of the most significant performance problems in the system. We found that we were calling Enum.ToString() 50,000 times just to bring up our login screen! And that was just the tip of the iceburg - Enum.ToString() is being called literally millions of times in any given user session, adding noticable lag times to many operations in the system.


So what to do about it? The first thing that comes to mind, from traditional object oriented programming, is to override the ToString() method of the System.Enum class (the C# enum keyword being mere syntax sure on top of System.Enum), but attempts to do so are greeted with the compilation error, "Cannot derive from special class System.Enum".

Another simple solution would be to use .NET 3.5 extension methods:

using System;

namespace FastEnumToString

{

    public enum Color

    {

        Red,

        White,

        Blue,

        Green

    }

    

    static class Extensions

    {

        public static string FastToString(this Color color)

        {

            switch (color)

            {

                case Color.Red: return "Red"; break;

                case Color.Green: return "Green"; break;

                case Color.Blue: return "Blue"; break;

                case Color.White: return "White"; break;

                default: return "Undefined"; break;

            }

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            var start = DateTime.Now;

            var rand = new Random();

            for (int i = 1; i < 1000000; i++)

            {

                var color = (Color)rand.Next(4);

                string str = color.ToString();

            }

            TimeSpan span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);

            Console.WriteLine("ToString() took " + span.ToString());

            start = DateTime.Now;

            for (int i = 1; i < 1000000; i++)

            {

                var color = (Color)rand.Next(4);

                string str = color.FastToString();

            }

            span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);

            Console.WriteLine("FastToString() took " + span.ToString());

            Console.ReadLine();

        }

    }

}

The result is much faster:

ToString() took 00:00:02.5550000

FastToString() took 00:00:00.0680000

0.068 seconds for a million FastToString() calls, faster by a factor of 38. This is great, but the extension method code written above is tedious and error prone. When the next developer comes along and adds another color to the enumeration, he or she will have to remember to add it to the extension method. And any typo in the enumeration strings could produce difficult to find bugs down the road, especially if the code is using Enum.Parse to convert the string back into a Color.

This would be a good use of CodeDOM or some other code generation technique to automatically generate the extension methods, but here's another solution that can be coded directly:

using System;

using System.Globalization;

namespace FastEnumToString

{

    public enum Color

    {

        Red,

        White,

        Blue,

        Green

    }

    static class Extensions

    {

        private static class EnumStrings<T>

        {

            private static string[] _strings;

            static EnumStrings()

            {

                if (typeof(T).IsEnum)

                {

                    _strings = new string[Enum.GetValues(typeof(T)).Length];

                    foreach (System.Enum value in Enum.GetValues(typeof(T)))

                    {

                        _strings[((IConvertible)value).ToInt32(CultureInfo.InvariantCulture)] = value.ToString();

                    }

                }

                else

                {

                    throw new Exception("Generic type must be an enumeration");

                }

            }

            public static string GetEnumString(int enumValue)

            {

                return _strings[enumValue];

            }

        }

        public static string FastToString(this Color color)

        {

            return EnumStrings<Color>.GetEnumString((int)color);

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            var start = DateTime.Now;

            var rand = new Random();

            for (int i = 1; i < 1000000; i++)

            {

                var color = (Color)rand.Next(4);

                string str = color.ToString();

            }

            TimeSpan span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);

            Console.WriteLine("ToString() took " + span.ToString());

            start = DateTime.Now;

            for (int i = 1; i < 1000000; i++)

            {

                var color = (Color)rand.Next(4);

                string str = color.FastToString();

            }

            span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);

            Console.WriteLine("FastToString() took " + span.ToString());

            Console.ReadLine();

        }

    }

}

The results are quite good, roughly the same as the hardcoded solution:

ToString() took 00:00:02.6770000

FastToString() took 00:00:00.0580000

The idea is simple: we want to store the string descriptions of the enumerations in an array the first time they are accessed, so we have our FastToString extension method read them out of a static class that builds the array in its constructor. We use a generic static class because that's a convenient way to get the compiler to create a separate array for each type of enumeration.

Note the "if (typeof(T).IsEnum)" line in the constructor. We would dearly have liked to restrict the generic type with a where T : System.Enum constraint. That would have allowed us to write a single generic extension method for all enumerations:

        public static string FastToString<T>(this T value) where T : System.Enum

        {

            return EnumStrings<T>.GetEnumString((int)value);

        }

but when we try it we're greeted again with our friendly compiler error, "Constraint cannot be special class 'System.Enum'". Because of this I'm forced to enforce the generic type with a runtime exception (and it is a runtime exception - try replacing <Color> with <string> in the FastToString method and see what happens). This exception makes EnumStrings<T> a dangerous class to use, which is why I've made it a rare example of a private, nested class. It is essentially an un-type safe class, because the compiler is unable to enforce its type-safe use.

So each developer that creates an enumeration will need to add a corresponding FastToString overload for their enumeration, but at least they don't have to add or maintain the error-prone switch statement.

One more thing to mention before continuing: we can't remove the "where T : System.Enum" constraint in the FastToString overload above because there's no guarantee that the input parameter "value" can be cast to an int, but we can do any of these things:

        public static string FastToString<T>(this T value) where T : IConvertible

        {

            return EnumStrings<T>.GetEnumString(((IConvertible)value).ToInt32(CultureInfo.InvariantCulture));

        }

        public static string FastToString<T>(this System.Enum value)

        {

            return EnumStrings<T>.GetEnumString(((IConvertible)value).ToInt32(CultureInfo.InvariantCulture));

        }

        public static string FastToString<T>(this int value)

        {

            return EnumStrings<T>.GetEnumString(value);

        }

The client calls for these look like this:

string str = color.FastToString<Color>();

string str = ((Enum)color).FastToString<Color>();

string str = ((int)color).FastToString<Color>();

Which result in the following timings:

ToString() took 00:00:02.6130000

FastToString() took 00:00:00.0700000

FastToString<T>(this T value) took 00:00:00.3820000

FastToString<T>(this Enum value) took 00:00:00.3690000

FastToString<T>(this int value) took 00:00:00.0710000

The two overloads that involve use of IConvertible are about 5 times slower than the one that doesn't. This is most likely due to the boxing that takes places when casting the enum to IConvertible. It is, however, still 7 times faster than ToString().

But I wouldn't recommend using any of the overloads that include the generic parameter, because they expose EnumString<T>'s type-unsafety to the caller. Because of .NET's current limitation against using System.Enum as a base class constraint on T, there's no way for us to prevent the client from calling FastString<Color> on some other enumeration type, or from making a (5).FastString<Color> call, or even from calling FastString<string>.

The final thing to note is that the above code won't work on enumerations that don't use the default enumeration value - integer mapping, e.g.

    public enum Color

    {

        Red = 2,

        White = 4,

        Blue = 8,

        Green = 16

    }

even this is legal:

    public enum Color

    {

        Red = -1,

        White = -1,

        Blue = -1,

        Green = -1

    }

One way to handle these cases is to use a Dictionary to store the enumeration-string mapping rather than an array. We would expect dictionary lookups to be slower than array lookups, so we'll continue to use arrays for the "standard" enumerations.

using System;

using System.Globalization;

using System.Collections.Generic;

namespace FastEnumToString

{

    public enum Color

    {

        Red = -1,

        White = -1,

        Blue = -1,

        Green = -1

    }

    public enum State

    {

        Oregon,

        Washington,

        California,

        Idaho

    }

    static class Extensions

    {

        private static class EnumStrings<T>

        {

            private static string[] _strings = null;

            private static Dictionary<int, string> _stringDictionary = null;

            private static bool IsStandardSequence(Array values)

            {

                List<Enum> valuesList = new List<Enum>();

                foreach (Enum value in values)

                {

                    valuesList.Add(value);

                }

                valuesList.Sort();

                for (int i = 0; i < 1000000; i++)

                {

                    if (((IConvertible)valuesList[i]).ToInt32(CultureInfo.InvariantCulture) != i)

                    {

                        return false;

                    }

                }

                return true;

            }

            static EnumStrings()

            {

                if (typeof(T).IsEnum)

                {

                    if (IsStandardSequence(Enum.GetValues(typeof(T))))

                    {

                        _strings = new string[Enum.GetValues(typeof(T)).Length];

                         foreach (System.Enum value in Enum.GetValues(typeof(T)))

                        {

                            _strings[((IConvertible)value).ToInt32(CultureInfo.InvariantCulture)] = value.ToString();

                        }

                    }

                    else

                    {

                        _stringDictionary = new Dictionary<int, string>();

                        foreach (System.Enum value in Enum.GetValues(typeof(T)))

                        {

                            int valueAsInt = ((IConvertible)value).ToInt32(CultureInfo.InvariantCulture);

                            if (!_stringDictionary.ContainsKey(valueAsInt))

                                _stringDictionary.Add(valueAsInt, value.ToString());

                        }

                    }

                }

                else

                {

                    throw new Exception("Generic type must be an enumeration");

                }

            }

            public static string GetEnumString(int value)

            {

                string description;

                if (_strings != null)

                {

                    description = _strings[(int)value];

                }

                else

                {

                    _stringDictionary.TryGetValue(value, out description);

                }

                return description;

            }

        }

        public static string FastToString(this Color color)

        {

            return EnumStrings<Color>.GetEnumString((int)color);

        }

        public static string FastToString(this State state)

        {

            return EnumStrings<State>.GetEnumString((int)state);

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            var start = DateTime.Now;

            var rand = new Random();

            for (int i = 1; i < 1000000; i++)

            {

                var color = (Color)rand.Next(4);

                string str = color.ToString();

            }

            TimeSpan span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);

            Console.WriteLine("ToString() took " + span.ToString());

            start = DateTime.Now;

            for (int i = 1; i < 1000000; i++)

            {

             var color = (Color)rand.Next(4);

                string str = color.FastToString();

            }

            span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);

            Console.WriteLine("FastToString() using dictionary took " + span.ToString());

            start = DateTime.Now;

            for (int i = 1; i < 1000000; i++)

            {

                var state = (State)rand.Next(4);

                string str = state.FastToString();

            }

            span = new TimeSpan(DateTime.Now.Ticks - start.Ticks);

            Console.WriteLine("FastToString() using array took " + span.ToString());

            Console.ReadLine();

        }

    }

}

Here are the results:

ToString() took 00:00:02.986000

FastToString() using dictionary took 00:00:00.0900000

FastToString() using array took 00:00:00.0640000

So the dictionary lookup doesn't appear to be significantly slower than the array lookup, but that may be skewed by the fact that this dictionary contains only one element.

Speaking of which, if you're looking at this line

                            if (!_stringDictionary.ContainsKey(valueAsInt))

                                _stringDictionary.Add(valueAsInt, value.ToString());

and thinking, "Aha! A bug: in the example above only one color ever gets added to the dictionary, so FastGetString() called on any color returns the same value!" you are correct, but this is simply mirroring the behavior of .NET enums. When enumeration values share the same underlying integral value, ToString() on either produces the same result:

            Console.WriteLine("Color.Red.ToString() = " + Color.Red.ToString());

            Console.WriteLine("Color.White.ToString() = " + Color.White.ToString());

            Console.WriteLine("Color.Blue.ToString() = " + Color.Blue.ToString());

            Console.WriteLine("Color.Green.ToString() = " + Color.Green.ToString());

            Console.WriteLine("Color.Red.FastToString() = " + Color.Red.FastToString());

            Console.WriteLine("Color.White.FastToString() = " + Color.White.FastToString());

            Console.WriteLine("Color.Blue.FastToString() = " + Color.Blue.FastToString());

            Console.WriteLine("Color.Green.FastToString() = " + Color.Green.FastToString());

            Console.ReadLine();

results in this, which is surprising.

Color.Red.ToString() = White

Color.White.ToString() = White

Color.Blue.ToString() = White

Color.Green.ToString() = White

Color.Red.FastToString() = White

Color.White.FastToString() = White

Color.Blue.FastToString() = White

Color.Green.FastToString() = White

So it is possible to come up with a viable workaround for the incredible slowness of Enum.ToString() in .NET, and for all of the other obstacles to extending System.Enum that .NET throws in its way, though it would have been much easier if .NET had simply made Enum.ToString() fast in the first place.


Followers