3
votes

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.)

3
The answer to question 1 is simple: it's not built yet. This isn't a C# limitation; the CLR itself has no support for such constraints and would need to be extended to make it possible. It's not one of those features that's so awesome people are chomping at the bit to put it in. (For starters, imagine new(int, int, bool, int, bool) -- it may meet the form of one of your constructors, but good luck figuring out what that constructor's supposed to do.) - Jeroen Mostert
A simple fix would be to use the new() constraint and to use the default constructor(add it). Then use the Index property: public static T validate<T>(int index) where T : A, new() { if (validation) return null; T t = new T(); t.Index = index; return t; } Otherwise you could use Activator.CreateInstance like here - Tim Schmelter
Good news - they wan't to build it. See this post: github.com/dotnet/csharplang/issues/769 . This post also contains a possible solution/workaround. - Mafii

3 Answers

5
votes

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.

5
votes

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));
    }
}
2
votes

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);