Reputation: 183
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
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
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
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