Stécy
Stécy

Reputation: 12349

In C#, How to create a define and use a class hierarchy derived from an enum value?

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

Answers (2)

Jodrell
Jodrell

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

Jon Skeet
Jon Skeet

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

Related Questions