Reputation: 12349
Given an enum type:
public enum Work
{
Normal,
Extended
}
What I would like to do is the following.
public abstract class Builder<T>
{
public static Builder<T> GetBuilder<T> (T work)
{
return new Builder<T> ();
}
}
public class BuilderNormal : Builder<Work.Normal>
{
}
public class BuilderExtended : Builder<Work.Extended>
{
}
I specifically want to avoid using a switch/case in Builder or using a mapping that I would need to maintain when I would add a new enum value to Work, i.e. I could do this
public abstract class Builder
{
public static Builder GetBuilder (Work work)
{
switch (work)
{
case Work.Normal:
return new BuilderNormal ();
case Work.Extended:
return new BuilderExtended ();
default:
throw new ...
}
}
}
So, basically, I want to create an instance of a class depending on an enum value and the class must be a child class of an abstract class.
Upvotes: 0
Views: 160
Reputation: 35716
You could do something pointless, crazy and slow like
public abstract class Builder
{
public static TBuilder GetBuilder<TBuilder>() where TBuilder : Builder
{
var ctors = typeof(TBuilder).GetConstructors(
BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public);
var matchingCtor = ctors.Single(
ci =>
{
var paramInfo = ci.GetParameters();
if (paramInfo.Length != parameters.Length)
{
return false;
}
return !paramInfo.Where((t, i) =>
t.ParameterType != parameters[i].GetType()).Any();
});
return (TBuilder)matchingCtor.Invoke(parameters);
}
}
which would give you a kind of static generic instance constructor, so you could do,
var builderNormal = Builder.GetBuilder<BuilderNormal>();
but, why not just call the instance constructor directly?
Upvotes: 0
Reputation: 1500785
You can't in the way you've designed, basically. Generic type parameters are always for types, not values.
What you can certainly do is maintain a single Dictionary<Work, Func<Builder>>
to allow you to basically register factories. That will avoid the switch statement, but it's still somewhere that you could forget to add values.
I'd rely on unit tests to avoid the problem though - write a test which checks that you can create a Builder
for every value within the enum; then if you ever add a value to the enum without adding a mapping, your test will fail.
EDIT: Another option would be to add an attribute to the enum values to say which builder type corresponds to that value. You'd then need to extract that type with reflection and instantiate it that way.
Upvotes: 6