6

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
  • 1
    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.) Commented Jul 28, 2017 at 10:03
  • 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 Commented Jul 28, 2017 at 10:04
  • 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. Commented Jul 28, 2017 at 10:05

3 Answers 3

9

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.

Sign up to request clarification or add additional context in comments.

Comments

5

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

3 Comments

Absolutely. Btw: your instantiator is also known as a factory method and should probably be named factory, as most programmers do.
@Mafii correct, but I named the parameter that way to mirror the naming of Activator & make the pattern a bit more obvious.
Fair enough, makes sense, I +1'd, since it's the cleanest/only compile time safe solution.
4

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.