2017-07-28 12 views
2

ジェネリック型を初期化します。Cannot create an instance of the variable type 'Item' because it does not have the new() constraintが非空のコンストラクタとその質問への参照

を私は非空のコンストラクタを持つジェネリック型からインスタンスを作成したいと思います。

'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:なぜ私は唯一のパラメータなしのコンストラクタを使用することができ、私はB b = validate<B>(42);を呼び出そう

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

私はこのエラーを得ましたか。

質問2:デリゲートやインターフェイスなしで問題を解決するにはどうすればよいですか? (私は常に幻想的な解決策を探しています)

+1

質問1への回答は簡単です:[まだ作成されていません](https://github.com/dotnet/roslyn/issues/2206)。これはC#の制限ではありません。 CLR自体はそのような制約をサポートしておらず、可能にするために拡張する必要があります。 (int、int、int、bool、int、bool)を想像してみてください。これはあなたのものの1つの形式を満たすかもしれません。そのコンストラクタが何をすべきかを理解することを幸運にしています。 –

+0

単純な修正は 'new()'制約を使い、デフォルトのコンストラクタ(add)を使うことです。次に、 'Index'プロパティを使用します。' public static T validate (int index) T:A、new() { if(検証) を返します。 T t = new T(); t.Index = index; return t; } 'それ以外の場合は、[ここ](https://stackoverflow.com/a/15003163/284240)のようなActivator.CreateInstanceを使用することができます –

+0

良いニュース - 彼らはそれを構築することはできません。この記事を参照してください:https://github.com/dotnet/csharplang/issues/769。このポストには、可能な解決策/回避策も含まれています。 – Mafii

答えて

3

あなたはコンストラクタを呼び出すためにリフレクションを使用することができますが、まだあります:(https://stackoverflow.com/a/13523659/5962841から適応)

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


それとも、あなたができますアクティベータを使用してインスタンスを作成する(@ datraxの回答を参照)

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

あなたが求めている機能は、すでによくリクエストされています。

この機能のリクエストはここ(github csharp design repo)で追跡されていますが、今年は期待していませんが、試作も受け入れられていません。

1

ジェネリック型のコンストラクタに引数を渡すことはできません。唯一可能な制約は、T:new()です。 しかし、いずれにせよ、このようなインスタンスを作成する可能性

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

ここの問題は、実際にはC#のジェネリックには役立たない。

ジェネリック型は限り特性&方法として共通インタフェースを有していなければならない、すなわちTを特定の全体のポイントは、AのインスタンスまたはA.ここ

のサブクラスでなければなりませんだ、Aはありません。 intコンストラクタを持ちますが、intコンストラクタを持つサブクラスのインスタンスを作成したいとします。

また、あなたのコードから、BはAから継承されませんが、汎用メソッドでT:Aを指定しました。それは単なる監督ですか?

アクティベータの使用はレイトバインディングにも依存します。したがって、適切なコンストラクタがないクラスに対して呼び出すと、実行時エラーが発生します。

機能的アプローチは、より直感的で、より洗練されたものであり、実行時リフレクションに依存しません。

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:インスタンシエータはファクトリメソッドとも呼ばれ、ほとんどのプログラマがそうであるように、おそらく 'factory'という名前にする必要があります。 – Mafii

+0

@Mafiiは正しいですが、Activatorの名前を反映させて、パターンを少しだけ明瞭にするためにパラメータに名前を付けました。 –

+0

コンパイル時に最も安全なソリューションであるため、十分に公正であり、私は+1しました。 – Mafii

関連する問題