C# がクラス コンストラクターで暗黙のジェネリック型をサポートしないのはなぜですか?

StackOverflow https://stackoverflow.com/questions/45604

  •  09-06-2019
  •  | 
  •  

質問

C# では、コンパイラがジェネリック型パラメーターを推論できる場合は、ジェネリック型パラメーターを指定する必要はありません。たとえば、次のようになります。

List<int> myInts = new List<int> {0,1,1,
    2,3,5,8,13,21,34,55,89,144,233,377,
    610,987,1597,2584,4181,6765};

//this statement is clunky
List<string> myStrings = myInts.
    Select<int,string>( i => i.ToString() ).
    ToList<string>();

//the type is inferred from the lambda expression
//the compiler knows that it's taking an int and 
//returning a string
List<string> myStrings = myInts.
    Select( i => i.ToString() ).
    ToList();

これは、型パラメータが何であるかわからない匿名型に必要です (インテリセンスでは次のように表示されます)。 'a)コンパイラによって追加されるためです。

クラスレベルの型パラメータではこれを行うことはできません。

//sample generic class
public class GenericDemo<T> 
{
    public GenericDemo ( T value ) 
    {
        GenericTypedProperty = value;
    }

    public T GenericTypedProperty {get; set;}
}

//why can't I do:
int anIntValue = 4181;
var item = new GenericDemo( anIntValue ); //type inference fails

//however I can create a wrapper like this:
public static GenericDemo<T> Create<T> ( T value )
{
    return new GenericDemo<T> ( value );
}

//then this works - type inference on the method compiles
var item = Create( anIntValue );

C# がこのクラス レベルのジェネリック型推論をサポートしないのはなぜですか?

役に立ちましたか?

解決

実際、あなたの質問は悪くありません。私はここ数年、ジェネリック プログラミング言語をいじってきました。実際に開発することになったことはありませんが (おそらく今後も開発することはないでしょう)、ジェネリック型推論についてはよく考えてきました。私の最優先事項の 1 つは、ジェネリック型を指定せずにクラスを構築できるようにするためです。

C# には、これを可能にする一連のルールが欠如しています。開発者はこれを含める必要性をまったく理解していなかったように思います。実際、次のコードはあなたの提案に非常に近く、問題を解決します。C# に必要なのは、構文サポートの追加だけです。

class Foo<T> {
    public Foo(T x) { … }
}

// Notice: non-generic class overload. Possible in C#!
class Foo {
    public static Foo<T> ctor<T>(T x) { return new Foo<T>(x); }
}

var x = Foo.ctor(42);

このコードは実際に動作するため、問題はセマンティクスの問題ではなく、単にサポートの不足の問題であることがわかりました。以前の投稿を撤回する必要があるようです。;-)

他のヒント

C# がこのクラス レベルのジェネリック型推論をサポートしないのはなぜですか?

なぜならそれらは一般的に曖昧だからです。対照的に、関数呼び出しの場合は型推論が簡単です (すべての型が引数に含まれている場合)。しかし、コンストラクター呼び出し (議論のために誇張された関数) の場合、コンパイラーは複数のレベルを同時に解決する必要があります。1 つのレベルはクラス レベルで、もう 1 つはコンストラクターの引数レベルです。これを解決するのはアルゴリズム的に簡単ではないと思います。直感的には、NP 完全であるとさえ言えます。

解決が不可能な極端なケースを説明するために、次のクラスを想像して、コンパイラが何をすべきかを教えてください。

class Foo<T> {
    public Foo<U>(U x) { }
}

var x = new Foo(1);

Konrad さん、ありがとう。それは良い回答 (+1) ですが、さらに詳しく説明します。

C# に明示的なコンストラクター関数があるとしましょう。

//your example
var x = new Foo( 1 );

//becomes
var x = Foo.ctor( 1 );

//your problem is valid because this would be
var x = Foo<T>.ctor<int>( 1 );
//and T can't be inferred

最初のコンストラクターが推論できないというのはまさにその通りです。

さあ、クラスに戻りましょう

class Foo<T> 
{
    //<T> can't mean anything else in this context
    public Foo(T x) { }
}

//this would now throw an exception unless the
//typeparam matches the parameter
var x = Foo<int>.ctor( 1 );

//so why wouldn't this work?
var x = Foo.ctor( 1 );

もちろん、コンストラクターを (代替型を使用して) 再度追加すると、通常のメソッドのオーバーロードが解決できなかったかのように、あいまいな呼び出しが行われます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top