Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Recently, I've come to prefer mapping 1-1 relationships using Dictionaries instead of Switch statements. I find it to be a little faster to write and easier to mentally process. Unfortunately, when mapping to a new instance of an object, I don't want to define it like this:

var fooDict = new Dictionary<int, IBigObject>()
{
    { 0, new Foo() }, // Creates an instance of Foo
    { 1, new Bar() }, // Creates an instance of Bar
    { 2, new Baz() }  // Creates an instance of Baz
}

var quux = fooDict[0]; // quux references Foo

Given that construct, I've wasted CPU cycles and memory creating 3 objects, doing whatever their constructors might contain, and only ended up using one of them. I also believe that mapping other objects to fooDict[0] in this case will cause them to reference the same thing, rather than creating a new instance of Foo as intended. A solution would be to use a lambda instead:

var fooDict = new Dictionary<int, Func<IBigObject>>()
{
    { 0, () => new Foo() }, // Returns a new instance of Foo when invoked
    { 1, () => new Bar() }, // Ditto Bar
    { 2, () => new Baz() }  // Ditto Baz
}

var quux = fooDict[0](); // equivalent to saying 'var quux = new Foo();'

Is this getting to a point where it's too confusing? It's easy to miss that () on the end. Or is mapping to a function/expression a fairly common practice? The alternative would be to use a switch:

IBigObject quux;
switch(someInt)
{
    case 0: quux = new Foo(); break;
    case 1: quux = new Bar(); break;
    case 2: quux = new Baz(); break;
}

Which invocation is more acceptable?

  • Dictionary, for faster lookups and fewer keywords (case and break)
  • Switch: More commonly found in code, doesn't require the use of a Func<> object for indirection.
share|improve this question
2  
without the lambda you'll have the same instance being returned each time you do the lookup with the same key (as in fooDict[0] is fooDict[0]). with both the lambda and the switch this is not the case –  ratchet freak Oct 18 '12 at 14:13
    
@ratchetfreak Yes, I actually did realize this as I was typing up the example. I think I made a note of it somewhere. –  KChaloux Oct 18 '12 at 14:16
1  
I guess the fact that you explicitely put it in a Constant mean that you need the created object to be mutable. But if one day you can make them immutable, then returning the object directly will be the best solution. You can put the dict in a const field and only incur the cost of creation once in the whole application. –  Laurent Bourgault-Roy Feb 10 at 16:07

4 Answers 4

up vote 6 down vote accepted

That's an interesting take on the factory pattern. I like the combination of the Dictionary and the lambda expression; it made me look at that container in a new way.

I'm ignoring the concern in your question about CPU cycles as you mentioned in the comments that the non-lambda approach doesn't provide what you need.

I think either approach (switch vs. Dictionary + lambda) is going to be fine. The only limitation is that by using the Dictionary, you are limiting the types of inputs you could receive in order to generate the returned class.

Using a switch statement would provide you more flexibility on input parameters. However, if this got to be an issue, you could wrap the Dictionary inside of a method and have the same end result.

If it's new for your team, comment the code and explain what's going on. Call for a team code review, walk them through what was done and make them aware of it. Other than that, it looks fine.

share|improve this answer
    
Unfortunately, as of about a month ago, my team consists solely of me (the lead quit). I didn't think of its relevance to the factory pattern. That's a neat observation, actually. –  KChaloux Oct 18 '12 at 14:27
1  
@KChaloux: Of course, if you were using just the Factory Method pattern, your case 0: quux = new Foo(); break; becomes case 0: return new Foo(); which is frankly as easy to write and much easier to read than { 0, () => new Foo() } –  pdr Oct 18 '12 at 16:19
    
@pdr That's shown up a few places in code already. There's probably a good reason to create a factory method on the object that inspired this question, but I figured it was interesting enough to ask on its own. –  KChaloux Oct 18 '12 at 16:21
1  
@KChaloux: I confess I'm not keen on the recent obsession with replacing switch/case with a dictionary. I've not seen a case yet where simplifying and isolating the switch in its own method wouldn't have been more effective. –  pdr Oct 18 '12 at 16:31
    
@pdr Obsession is a strong word to use here. More of a consideration when deciding how to deal with one-off mappings between values. I agree that in cases where it's repeated, its best to isolate a creational method. –  KChaloux Oct 18 '12 at 16:50

C# 4.0 gives you the Lazy<T> class, which is similar to your own second solution, but shouts out "Lazy initialization" more explicitly.

var fooDict = new Dictionary<int, Lazy<IBigObject>>()
{
    { 0, new Lazy(() => new Foo()) }, // Returns a new instance of Foo when invoked
    { 1, new Lazy(() => new Bar()) }, // Ditto Bar
    { 2, new Lazy(() => new Baz()) }  // Ditto Baz
}
share|improve this answer
    
Ooh, I didn't know that. –  KChaloux Oct 22 '12 at 12:35
    
Oh, that's nice! –  Laurent Bourgault-Roy Feb 10 at 16:04
1  
However, once Lazy.Value is invoked, it uses the same instance for its lifetime. See Lazy Initialization –  Dan Lyons Feb 10 at 18:06
    
Of course, otherwise it wouldn't be lazy initialization, just re-initialization every time. –  Avner Shahar-Kashtan Feb 10 at 18:07
    
The OP states that he needs it to create new instances each time. The second solution with lambdas and the third solution with a switch both do that, while the first solution and the Lazy<T> implementation do not. –  Dan Lyons Feb 10 at 18:11

Stylistically I think the readability is equal between them. It's easier to do dependency injection with the Dictionary.

Don't forget that you must check whether the key exists when using the Dictionary, and must provide a fallback if it does not.

I would prefer the switch statement for static code paths, and the Dictionary for dynamic code paths (where you might add or remove entries). The compiler might be able to perform some static optimizations with the switch that it cannot with the Dictionary.

Interestingly, this Dictionary pattern is what people sometimes do in Python, because Python lacks the switch statement. Otherwise they use if-else chains.

share|improve this answer

In general, I would prefer neither.

Whatever is consuming this should work with a Func<int, IBigObject>. Then the source of your mapping can be a Dictionary or a method that has a switch statement or a web service call or some file lookup... whatever.

As for implementation, I'd prefer the Dictionary since that is more easily refactored from 'hard code dictionary, lookup key, return result' to 'load dictionary from file, lookup key, return result'.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.