2017-07-28 87 views
2

引用到这个问题:Cannot create an instance of the variable type 'Item' because it does not have the new() constraint初始化泛型类型非空的构造

我想从一个普通型与非空的构造函数创建一个实例。

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

如果我尝试致电B b = validate<B>(42);我得到这个错误:

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

问题1:为什么我只能用一个参数构造函数?

问题2:如何解决我的问题,无需委托或接口? (我一直在寻找奇特的解决方案。)

+1

问题1的答案很简单:[它尚未构建](https://github.com/dotnet/roslyn/issues/2206)。这不是C#的限制; CLR本身不支持这些限制,需要扩展才能实现。它不是那些令人敬畏的功能,也不是那些令人敬畏的功能之一。(对于初学者,想象一下'new(int,int,bool,int,bool)' - 它可能符合你的一种形式构造函数,但好运算出构造函数应该*做*。) –

+0

一个简单的解决方法是使用'new()'约束并使用默认构造函数(添加它)。然后使用'Index'属性:'public static T validate (int index) 其中T:A,new() if(validation) return null; T t = new T(); t.Index = index; return t; }'否则你可以使用'Activator.CreateInstance'就像[here](https://stackoverflow.com/a/15003163/284240) –

+0

好消息 - 他们不想构建它。看到这篇文章:https://github.com/dotnet/csharplang/issues/769。这篇文章还包含一个可能的解决方案/解决方法。 – Mafii

回答

3

您可以使用反射调用构造函数:

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

(改编自https://stackoverflow.com/a/13523659/5962841


或者,你可以使用激活器来创建实例(请参阅@ datrax的答案)

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

你问已经经常被要求的功能。

这里正在追踪该功能的请求(github csharp design repo),但不要指望它今年,它甚至没有原型或接受。

1

您不允许将任何参数传递给泛型类型的构造函数。唯一可能的约束是:where T:new()。 但无论如何还是有可能创造这样的实例

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

这里的问题并不适用于C#泛型。

泛型类型应该有一个共同的接口,只要性能&方法,这是指定T必须是A的实例或A的一个子类的整点

这里,A不具有int构造函数,但是你想创建一个具有int构造函数的子类的实例。

此外,从您的代码中,B不从A继承,但您在泛型方法中指定了T:A.这只是一个疏忽吗?

使用Activator也依赖于后期绑定,所以如果你为没有合适的构造函数的类调用它,你会得到一个运行时错误。

功能方法更直观,看起来更干净,并且不依赖运行时反射。

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

绝对如此。 Btw:你的'instantiator'也被称为工厂方法,应该像大多数程序员一样,可能被命名为'factory'。 – Mafii

+0

@Mafii是正确的,但我命名参数的方式来反映Activator的命名,并使模式更加明显。 –

+0

公平,有道理,我+ 1'd,因为它是最干净的/唯一编译时安全的解决方案。 – Mafii