2

私はこの記事を通して自分の道を歩もうとしてきました:

http://blogs.msdn.com/wesdyer/archive/2008/01/11/the-marvels-of-monads.aspx

...そして、1ページ目の何かが私を不快にさせました。特に、私は Compose<>() 関数に頭を悩ませようとしていたので、自分用に例を書きました。次の 2 つの Func を考えてみましょう。

Func<double, double> addTenth = x => x + 0.10;
Func<double, string> toPercentString = x => (x * 100.0).ToString() + "%";

問題ない!この2つが何をするのかを理解するのは簡単です。

ここで、記事の例に従って、次のように、これらの関数を構成する汎用拡張メソッドを記述できます。

public static class ExtensionMethods
{
 public static Func<TInput, TLastOutput> Compose<TInput, TFirstOutput, TLastOutput>(
  this Func<TFirstOutput, TLastOutput> toPercentString,
  Func<TInput, TFirstOutput> addTenth)
 {
  return input => toPercentString(addTenth(input));
 }
}

罰金。だから今、あなたは言うことができます:

string x = toPercentString.Compose<double, double, string>(addTenth)(0.4);

そして、文字列「50%」を取得します

ここまでは順調ですね。

しかし、ここにはあいまいなことがあります。別の拡張メソッドを作成すると、次の 2 つの関数が作成されたとします。

public static class ExtensionMethods
{
 public static Func<TInput, TLastOutput> Compose<TInput, TFirstOutput, TLastOutput>(
  this Func<TFirstOutput, TLastOutput> toPercentString,
  Func<TInput, TFirstOutput> addTenth)
 {
  return input => toPercentString(addTenth(input));
 }

 public static Func<double, string> Compose<TInput, TFirstOutput, TLastOutput>(this
  Func<double, string> toPercentString,
  Func<double, double> addTenth)
 {
  return input => toPercentString(addTenth(input + 99999));
 }
}

ここが曖昧です。これら 2 つの関数の署名が重複していませんか? はい。これでもコンパイルできますか?はい。どっちが呼ばれる?2番目のもの(明らかに「間違った」結果が得られます)が呼び出されます。いずれかの関数をコメント アウトしても、コンパイルは実行されますが、異なる結果が得られます。

つまらないことのようですが、ここには私の感性を深く怒らせる何かがあり、指を置くことはできません. 拡張メソッドと関係がありますか?ラムダと関係がありますか?それとも、 Func<> を使用して戻り値の型をパラメーター化する方法と関係がありますか? わからない。

これはすべて仕様のどこかで対処されていると思いますが、これを見つけるためにGoogleが何をすべきかさえわかりません.

ヘルプ!

4

2 に答える 2

6

あいまいなことは何もありません。2 番目のものは、完全に一致するたびに呼び出されます。正確に一致しない場合は常に、最初の関数が取得されます。これは、デフォルトでは他のすべてのものと完全に一致するためです。

を作成しFunc<double, string>、別のFunc<double, double>.Compose を明示的に指定して .Compose を呼び出し<double, double, string>た場合、コンパイラは 2 番目のバージョンが完全に一致することを判断するのに十分な情報を持っているため、それが使用されます。

しかし、次のばかげた例を考えてみてください。

Func<string, string> doubleString = s => s + s;
Func<DateTime, string> dateToString = date => date.ToString();

Func<DateTime, string> composedFunction = doubleString.Compose(dateToString);
Console.WriteLine(composedFunction(DateTime.Now));

どのバージョンが呼び出されますか? 結果は?最初のバージョンで、出力は、それ自体に連結された文字列としての日付です。

一方、and を使用したより現実的な例Func<double, string>Func<double, double>あり、Compose の呼び出しが明示的でなかった場合、どのバージョンが呼び出されるのでしょうか?

Func<double, string> toPercentString = d => d.ToString("0.0%");
Func<double, double> addTenth = d => d + 0.1;
Console.WriteLine(toPercentString.Compose(addTenth)(0.8));

最初のものは、コンパイラが 2 番目のものと完全に一致すると判断するためです。私は Eric Lippert や Jon Skeet ではないので、そのバインディングについては説明しません。


static void DoSomething(float f, double d) { }
static void DoSomething(double d, float f) { }

...

DoSomething(1, 1);

これはあいまいです (そのためコンパイルできません)。

于 2010-04-24T05:45:25.200 に答える
0

これがデリゲート、ジェネリック、または拡張メソッドと何の関係があるのか​​ わかりません。あなたの根本的な問題は、オーバーロードにあるようです (具体的には、複数の候補がある場合のメソッドのオーバーロードの解決)。検討:

public static class Test {
    public static void Method(string s) { 
        Console.WriteLine("String version: " + s); 
    }
    public static void Method(object o) { 
        Console.WriteLine("Object version: " + o.ToString()); 
    }

    public static void Main(string[] args) { Method("my string"); }

}

Main、どのメソッドが呼び出されますか? あいまいさはありますか?コンパイル中にオーバーロードの解決が開始され、1 つのメソッドがより適していると判断されます (文字列を受け取るメソッド)。あなたの場合と同じように、最初のオーバーロードをコメントアウトすると、コンパイルされるが他のオーバーロードを呼び出すコードになります。2 番目のメソッドが次のように定義されている場合、同じことが起こります。

    public static void Method<T>(T t) { 
        Console.WriteLine("Generic version: " + t.ToString()); 
    }

これはオーバーロード解決の候補ですが、文字列を取るオーバーロードは完全一致であり、優先されます。

于 2010-04-26T14:09:11.347 に答える