Reputation: 7140
This is a bit of a weird one.
I'm building a web application and I was putting together a simple factory-pattern-like thing to handle some object instantiation. I don't like big ugly switch-case blocks, so I tend to do this:
public enum Option
{
Option1,
Option2,
Option3
}
public Dictionary<Option, Func<MyBaseClass>> OptionMapping
= new Dictionary<Option, Func<MyBaseClass>>
{
{ Option1, () => new DerivedClassOne() },
{ Option2, () => new DerivedClassTwo() },
{ Option3, () => new DerivedClassThree() }
};
and then the switch block becomes MyBaseClass selectedThing = OptionMapping[currentOption]();
.
It occurred to me while writing this code that if you ever had a situation where two options required the same action, you'd end up with the same Func<>
in two places, which I'd rather avoid. Assuming for the moment that you can't just refactor some logic to merge those two options into one, I'd like to know if there's a way to get one Func
to call the other.
My first thought was this:
public Dictionary<Option, Func<MyBaseClass>> OptionMapping
= new Dictionary<Option, Func<MyBaseClass>>
{
{ Option1, () => new DerivedClassOne() },
{ Option2, () => new DerivedClassTwo() },
{ Option3, () => this[Option1]() }
};
but a quick test in LINQPad tells me that doesn't work because of course the this
refers to the class in which the code is written, not the Dictionary
(apparently I've spent too much time in languages where this
is more fickle). My second thought was to change it to () => OptionMapping [Option1]()
, but that gives an error that you're trying to use OptionMapping
before it's initialised. You could of course do this:
public Dictionary<Option, Func<MyBaseClass>> OptionMapping
= new Dictionary<Option, Func<MyBaseClass>>
{
{ Option1, () => new DerivedClassOne() },
{ Option2, () => new DerivedClassTwo() }
};
OptionMapping.Add(Option3, () => OptionMapping [Option1]());
which works, but isn't as pretty. So, this question is mostly one of curiosity.
Is there a way to access one element of a Dictionary
from another? More generally, does C# have a keyword like this
that means "the object I'm inside right now at time of execution", rather than "the containing object at write time"?
EDIT:
Originally, the Dictionary
was static, so the error I mentioned about using it before it's been initialised wouldn't happen. The static
was actually a leftover from pasting in a larger block of code and editing it down, and then copy-pasting the error around.
So, the main answer being posted is about having a separate object to store the Func
and then adding that to the Dictionary
twice. That makes sense, and is more or less what I went with in the end (for various reasons it also made some of the surrounding code a bit cleaner) but it's kind of avoiding the part of the question I'm most interested in, which is whether it's possible in C# to access the object you're in at runtime, so that an object could access the dictionary (or list, or array, etc) that they're currently within.
Upvotes: 5
Views: 124
Reputation: 54801
I would minimise the use of statics like so:
Create an object to hold this mapping. It can also execute the function to actually instantiate the classes, i.e. actually execute the Func
. From the outside the caller does not know then how the mapping is performed allowing you to replace it later with a DI container or other method of construction: I've also added an interface to help you see how you could add other implementations and inject them at run time. This is also useful for testing.
public interface IOptionFactory
{
MyBaseClass Create(Option option);
}
public sealed class OptionFactory : IOptionFactory
{
private readonly Dictionary<Option, Func<MyBaseClass>> _optionMapping;
public OptionFactory()
{
Func<MyBaseClass> makeDerivedTwo = () => new DerivedClassTwo();
_optionMapping = new Dictionary<Option, Func<MyBaseClass>>
{
{ Option.Option1, () => new DerivedClassOne() },
{ Option.Option2, makeDerivedTwo },
{ Option.Option3, makeDerivedTwo }
};
}
public MyBaseClass Create(Option option)
{
return _optionMapping[option]();
}
}
Then you only need the one static which will keep that other class nice and clean:
private static IOptionFactory OptionMapping = new OptionFactory();
Then you need to ask yourself, do I need it to be static
? Can I inject the IOptionFactory
?
Here is an example of an implementation of IOptionFactory
might look when using Dependency Injection with Unity.
public sealed class UnityOptionFactory : IOptionFactory
{
private readonly IUnityContainer _container;
private readonly Dictionary<Option, Type> _optionMapping;
public UnityOptionFactory(IUnityContainer container)
{
var makeDerivedTwo = typeof(DerivedClassTwo);
_optionMapping = new Dictionary<Option, Type>
{
{ Option.Option1, typeof(DerivedClassOne) },
{ Option.Option2, makeDerivedTwo },
{ Option.Option3, makeDerivedTwo }
};
}
public MyBaseClass Create(Option option)
{
return _container.Resolve(_optionMapping[option]);
}
}
Advantage, Unity is creating those types, so you can inject into their constructors.
Upvotes: 1
Reputation: 726849
This should work:
private static Dictionary<Option, Func<MyBaseClass>> OptionMapping
= new Dictionary<Option, Func<MyBaseClass>> {
{ Option.Option1, () => new DerivedClassOne() },
{ Option.Option2, () => new DerivedClassTwo() },
{ Option.Option3, () => OptionMapping[Option.Option2]() }
};
The problem with this approach is that Option3
contains a different Func
object, which relies on another Func
to produce its output every time you call it.
I think this is not as nice as a solution in which you make a shared Func
object explicitly, and place it in the dictionary several times (i.e. your second code snippet, or the alternative below)
private static Dictionary<Option, Func<MyBaseClass>> OptionMapping;
static MyClass() {
Func<MyBaseClass> makeDerivedTwo = () => new DerivedClassTwo();
OptionMapping = new Dictionary<Option, Func<MyBaseClass>> {
{ Option.Option1, () => new DerivedClassOne() },
{ Option.Option2, makeDerivedTwo },
{ Option.Option3, makeDerivedTwo }
};
}
Upvotes: 2
Reputation:
Nothing like that you describe exists, for the general case. However, for your specific case, your dictionary is stored in a static
field, so you should have no problem accessing it through that field:
public static Dictionary<Option, Func<MyBaseClass>> OptionMapping
= new Dictionary<Option, Func<MyBaseClass>>
{
{ Option1, () => new DerivedClassOne() },
{ Option2, () => new DerivedClassTwo() },
{ Option3, () => OptionMapping[Option1]() }
};
In the question, you say:
My second thought was to change it to
() => OptionMapping [Option1]()
, but that gives an error that you're trying to use OptionMapping before it's initialised.
But that's not right. You won't get that error, not in this case. You could if it were a local variable, but in that case, you could still do:
Dictionary<Option, Func<MyBaseClass>> OptionMapping = null;
OptionMapping = new Dictionary<Option, Func<MyBaseClass>>
{
{ Option1, () => new DerivedClassOne() },
{ Option2, () => new DerivedClassTwo() },
{ Option3, () => OptionMapping[Option1]() }
};
The fact that OptionMapping
is initialised in that case is enough to avoid the problem. The initialiser doesn't have to be the dictionary you want to store.
Upvotes: 2