9

私はこのような公開機能を持っています:

public static T Get<T>(this Mango m, T defaultValue = default(T)) where T : class
{
    //do something; return something;
}

public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct
{
    //do something; return something;
}

基本的に、参照型とnull許容型を個別に処理したいと思います。コンパイルします。値型を要求するまで。参照型の場合はコンパイルします。

mango.Get<string>(); // compiles..
mango.Get(""); // compiles..

mango.Get<int>(); // The type 'int' must be a reference type in order to use it as 
                  // parameter 'T' in the generic type or method Get<T>(Mango, T)
//also            // The call is ambiguous between the following methods or properties: 
                  // Get<int>(Mango, int) and Get<int>(Mango, int?)

ここにはどのような本当の曖昧さがありますか?の場合、構造体オーバーロードを適切に呼び出すことはできませんかTintまた:

mango.Get<int>(0);  // The type 'int' must be a reference type in order to use it as 
                    // parameter 'T' in the generic type or method Get<T>(Mango, T)

コンパイラが参照型の過負荷のみを検出するのはなぜですか?私は2つの別々のオーバーロードを試してみました:

public static T Get<T>(this Mango m) where T : class
{
    return default(T);
}

public static T? Get<T>(this Mango m) where T : struct
{
    return default(T);
}

public static T Get<T>(this Mango m, T def) where T : class
{
    return default(T);
}

public static T? Get<T>(this Mango m, T? def) where T : struct
{
    return default(T);
}

問題は解決しませんでした。そして明らかに、オーバーロードは単に制約に基づいて機能しないため、最初の2つのメソッドはここではコンパイルされません。

次のように、制約されたオーバーロードを削除し、class制約されたオーバーロードだけを保持してみましたstruct

public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct
{
    //do something; return something;
}

mango.Get<int>(); // voila compiles!
mango.Get<int>(0); // no problem at all..
// but now I can't have mango.Get<string>() for instance :(

2つの関数の名前を変更するだけですか?呼び出し元が実装の詳細を気にする必要がなくGet、任意のタイプを呼び出すだけで済むように、名前を統一するのが適切だと思います。

更新:オプションのパラメーターを回避する必要がある場合、Marcのソリューションは機能しません。

mango.Get<int>(); // still wouldnt work!!

しかし、もっと魔法があります:( :(

public static bool IsIt<T>(this T? obj) where T : struct
{
    return who knows;
}

public static bool IsIt<T>(this T obj) where T : class
{
    return perhaps;
}

どうしても、同じコンパイラのバグ(私によると)が私を悩ませることを期待しています。しかし、今回は機能しません。

Guid? g = null;
g.IsIt(); //just fine, and calls the struct constrained overload
"abcd".IsIt(); //just fine, and calls the class constrained overload

したがって、Marcが言うように、制約チェックの前に過負荷の解決が行われる場合、今回も同じエラーが発生するのではないでしょうか。しかし、違います。なんでそうなの?一体何が起こっているのですか?:バツ

4

3 に答える 3

6

制約チェックは、過負荷解決、IIRCの後に行われます。過負荷の解決は最初のバージョンを好むようです。ただし、もう一方を使用するように強制することはできます。

mango.Get<int>((int?)0);

あるいは:

mango.Get((int?)0);

個人的には、あいまいさを避けるために名前を変更するだけでしょう。

于 2013-02-07T11:23:12.460 に答える
1

興味深いことに、コンパイラは、メソッドシグニチャ内で使用されるジェネリック型内で指定された制約をチェックしますが、シグニチャ自体内の制約についてはチェックしません。

したがって、メソッドが2つのパラメーターを受け入れた場合(1つは型T where T : structと一緒にNullable<T>[])、コンパイラーはT構造体ではないもののメソッドを考慮しません。メソッドで指定されたのstruct制約は、オーバーロードの評価では考慮されませんが、構造体に制約を与えるTという事実は考慮されます。Nullable<T>T

Nullable<T>[]パラメータにデフォルトのnull値を指定し、パラメータが存在しないふりをすることができることを考えると、過負荷評価で制約を考慮することがまったく不可能であることが本当にわかります。ただし、vb.netコンパイラとC#コンパイラは、あいまいと見なされるものと受け入れるものに関しては異なるようです。

于 2013-02-10T23:31:30.460 に答える
0

自分に答えてみよう。

marcが言うように、制約チェックは過負荷の解決後、および

public static T Get<T>(this Mango m, T defaultValue = default(T)) where T : class
{
    //do something; return something;
}

public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct
{
    //do something; return something;
}

過負荷解決はバージョンを優先しclassます。ただし、これは、コンパイラーに2つの類似したオーバーロードのいずれかを選択できる場合のみです(オプションのパラメーターがない場合、両方のオーバーロードは同じになり、制約は無視されます)。これで、制約が適用されると、はではないためGet<int>、呼び出しは失敗します。intclass

デフォルトのパラメータが提供されている場合、状況は少し変わります。電話したら

mango.Get(0);

コンパイラは適切なオーバーロードを呼び出すのに十分ですが、どのオーバーロードが受け入れるintT where T: struct?ありません。与えられた例では、2番目のパラメーターはであると予想され、ではT?ありませんTコンパイラーは、すべての引数タイプに使用可能なすべてのキャストを適用することによって、オーバーロードを自動的に解決しません。これはバグではありませんが、機能が1つ少なく、すべてです。私がこれを行う場合:

int? i = 0;
mango.Get(i);

それは機能し、適切なオーバーロードが呼び出されます。これは、2番目の例でも起こっていることです。私が正しいパラメータを提供しているので、それは機能します。

私が電話するとき:

Guid? g = null;
g.IsIt();

objであることがわかっているgため、Tに等しくなりGuidます。しかし、私が電話すると

Guid g = Guid.NewGuid();
g.IsIt();

これは機能しません。これは、g現在では機能せGuidGuid?、コンパイラーは自動的にキャストを実行しないためです。代わりに、コンパイラーに明示的に通知する必要があります。

可能なすべてのタイプを計算するには多すぎるため、コンパイラが自動的にキャストを行わないという事実は問題ありませんが、C#の欠陥と思われるのは、制約チェックがオーバーロードに関与していないという事実です。解像度。それは、私がのようなタイプを提供したとしても、mango.Get<int>()またはオーバーロードの解決がバージョンをmango.Get<int>(0)優先せず、引数に使用することはありません。私には奇妙に見えます。structdefault(int?)defaultValue

于 2013-02-10T12:36:18.073 に答える