Andrei Andreev
Andrei Andreev

Reputation: 773

Factory that takes constructors

How can I make a factory that behaves like this:

interface A {
}

class B: A {
 string b;
 public B(string b) { this.b = b; }
}

class Factory {
 public static T create<T>(/*somehow pass constructor parameters*/) where T: A {
  return new T(/*constructor parameters*/);
 }
}

B test = Factory.create<B>("test");

I know, I can just use class constructor, but this pattern may have some uses and I want to know if this is possible.

Upvotes: 0

Views: 98

Answers (3)

Jeroen Mostert
Jeroen Mostert

Reputation: 28809

You can do it, but it ain't pretty.

class Factory {
    public static T Create<T>(params object[] parameters) where T : A {
        return (T) Activator.CreateInstance(typeof(T), parameters);
    }
}

Although this may look superficially attractive, there are problems:

  • You will never know if a call works until runtime, where it can throw a nasty exception.
  • Conversion of parameters can make it hard to see which constructor will get called -- if any.
  • Activator.CreateInstance is very slow compared to new. You can get the constructor directly using Type.GetConstructor(), which is marginally faster, but it still sucks.

Solutions like this tend to be too clever for their own good. You are really much better off using static typing, even with factories. One alternative to a factory is accepting a Func<T> that will produce an instance on demand:

void needToUseAnAForSomething(Func<A> aCreator) {
   ...
   A a = aCreator();
}

And then client code can do something like this:

needToUseAnAForSomething(() => new B("test"));

In this case, of course, we could simply pass the instance directly, but in more complicated scenarios this could take the place of a factory construction.

Upvotes: 1

Akhil B
Akhil B

Reputation: 40

You can try something like this -

public static object create<T>(string param) where T : A
            {
                Type type = typeof(T);
                ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string) });
                object instance = ctor.Invoke(new object[] { param });

                return instance;
            }

Upvotes: 0

Residuum
Residuum

Reputation: 12064

This can bei achieved with reflection:

public static T Create<T> (object[] parameters) where T : class
{
    Type[] parameterTypes = (parameters ?? Enumerable.Empty<object> ())
        .Select (parameter => parameter.GetType ()).ToArray ();
    ConstructorInfo ctor = type.GetConstructor (parameterTypes);
    Debug.Assert (ctor != null);
    T instance = ctor.Invoke (parameters) as T;
    Debug.Assert (instance != null);
    return instance;
}

You Invoke that method by calling:

B test = Factory.Create<B> (new object[]{"test"});

Upvotes: 0

Related Questions