Syrlia
Syrlia

Reputation: 183

Initialize generic type with non-empty constructor

Referencing to that Question: Cannot create an instance of the variable type 'Item' because it does not have the new() constraint

I would like to create an instance from a generic type with a non empty constructor.

public class A{};

public class B
{
   public B(int){/*...*/}
}

public class C
{
   public static T validate<T>(int index) 
      where T : A, 
      new(int) //error
   {
      if(/*validation*/)
         return null;
      return new T(index);
   }
}

If I try to call B b = validate<B>(42); I got this error:

'B' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'C.validate(int)'

Question 1: Why can I use a parameterless constructors only?

Question 2: How can I solve my problem without delegates or interfaces? (I am always looking for fancy solutions.)

Upvotes: 6

Views: 7851

Answers (3)

Mafii
Mafii

Reputation: 7425

Update for C# 11

The new static abstract/virtual members in interfaces feature, designed with generic maths in mind, but usable to require static factory functions for your type, is a great fit and I would recommend using it if you can change the type implementation.

Just add a static abstract T Create(your params come here...) to an interface, and then add a constraint in the form T : IYourInterface to your method.

Then, just use T.Create(...) to create an instance. Amazing, isn't it?!


Old Answer

You can use reflection to invoke the constructor:

var constructorInfo = typeof(T).GetConstructor(new[] { typeof(int) });
if (constructorInfo != null)
{
    object[] parameters = new object[] { index };
    return (T)constructorInfo.Invoke(parameters);
}
else
{
  // handle it
}

(adapted from https://stackoverflow.com/a/13523659/5962841)


Or, you can use activator to create an instance (see @datrax's answer)

(T)Activator.CreateInstance(typeof(T), index)

The feature you asked for has already often been requested.

The request for the feature is being tracked here (github csharp design repo), but don't expect it this year, it's not even prototyped or accepted.

Upvotes: 9

Ashley Pillay
Ashley Pillay

Reputation: 888

The problem here does not really lend itself to C# generics.

A generic type should have a common interface as far as properties & methods, that's the whole point of specifying T must be an instance of A or a subclass of A.

Here, A doesn't have an int constructor, but you want to create an instance of a subclass that does have an int constructor.

Also, from you code, B doesn't inherit from A, but you specified T: A in your generic method. Is that just an oversight?

Using Activator also relies on late binding, so if you call it for a class that doesn't have an appropriate constructor, you'll get a runtime error.

A functional approach is more intuitive, looks cleaner, and doesn't rely on runtime reflection.

public class A {
};

public class B:A
{
    public B(int y){ x = y; }
    public int x { get; }
}

public class C
{
    public static T validate<T>(int index, Func<int, T> instantiator)
       where T : A
    {
        if (false)
            return null;

        return instantiator(index);
    }
}

class Program
{
    static void Main(string[] args)
    {
        B b = C.validate<B>(42, (y)=>new B(y));
    }
}

Upvotes: 5

datrax
datrax

Reputation: 51

You are not allowed to pass any argument to the constructor of generic type. The only possible constraint is: where T :new (). But anyway there's still a possibility to create such instance

(T)Activator.CreateInstance(typeof(T), index);

Upvotes: 4

Related Questions