2017-07-28 89 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