18

.NET 4.5 の一部である新しい Task.Run 静的メソッドは、期待どおりに動作しないようです。

例えば:

Task<Int32> t = Task.Run(()=>5);     

正常にコンパイルされますが、

Task<Int32> t = Task.Run(MyIntReturningMethod);
...
public Int32 MyIntReturningMethod() {
  return (5);
  }

MyIntReturningMethod が間違った型を返していると訴えています。

おそらく、Task.Run のどのオーバーロードが呼び出されているのか理解できていません。しかし、私の考えでは、上記のラムダ コードは によく似てFunc<Int32>おり、MyIntReturningMethod は確実に互換性があります。Func<Int32>

何が起こっているかについてのアイデアはありますか?マイケル

4

6 に答える 6

11

(もちろん、問題から抜け出すには、単に と言ってTask.Run((Func<int>)MyIntReturningMethod)ください。)

などとは一切関係ありませんTask

ここで注意すべき問題の 1 つは、非常に多くのオーバーロードが存在する場合、コンパイラエラー テキストが 1 つの "ペア" のオーバーロードだけに集中することです。それは紛らわしいです。その理由は、最適なオーバーロードを決定するアルゴリズムがすべてのオーバーロードを考慮し、そのアルゴリズムが最適なオーバーロードが見つからないと結論付けた場合、エラー テキストに対して特定のオーバーロードのペアを生成しないためです。関与してきました。

何が起こるかを理解するには、代わりにこの単純化されたバージョンを参照してください。

static class Program
{
    static void Main()
    {
        Run(() => 5);  // compiles, goes to generic overload
        Run(M);        // won't compile!
    }

    static void Run(Action a)
    {
    }
    static void Run<T>(Func<T> f)
    {
    }
    static int M()
    {
        return 5;
    }
}

ご覧のとおり、これには への参照はまったくありませんTaskが、それでも同じ問題が発生します。

無名関数の変換とメソッド グループの変換は (まだ) まったく同じではないことに注意してください。詳細はC# 言語仕様に記載されています。

ラムダ:

() => 5

実際にはSystem.Action型に変換することさえできません。あなたがしようとすると:

Action myLittleVariable = () => 5;

エラー CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement で失敗します。したがって、ラムダで使用するオーバーロードは非常に明確です。

一方、メソッド グループ:

M

Func<int>と の両方に変換可能Actionです。ステートメントのように、戻り値を取得しないことは完全に許可されていることに注意してください。

M(); // don't use return value

自体は有効です。

この種の答えは質問に答えますが、追加のポイントを作るために追加の例を挙げます. 例を考えてみましょう:

static class Program
{
    static void Main()
    {
        Run(() => int.Parse("5"));  // compiles!
    }

    static void Run(Action a)
    {
    }
    static void Run<T>(Func<T> f)
    {
    }
}

この最後の例では、ラムダは実際に両方のデリゲート型に変換可能です! (一般的なオーバーロードを削除してみてください。) ラムダ矢印の右側=>は次の式です。

int.Parse("5")

これは、それ自体でステートメントとして有効です。ただし、この場合でも、オーバーロードの解決により、より適切なオーバーロードを見つけることができます。前述したように、C# の仕様を確認してください。


HansPassant と BlueRaja-DannyPflughoeft に触発された、最後の (私が思う) 例を 1 つ示します。

class Program
{
    static void Main()
    {
        Run(M);        // won't compile!
    }

    static void Run(Func<int> f)
    {
    }
    static void Run(Func<FileStream> f)
    {
    }

    static int M()
    {
        return 5;
    }
}

この場合、 をint 5に変換する方法はまったくないことに注意してくださいSystem.IO.FileStream。それでもメソッド グループの変換は失敗します。これは、 が と の 2 つの通常のメソッドint f();を持っているという事実に関連している可能性がFileStream f();ありますf();。戻り値の型は、C# のメソッドのシグネチャの一部ではありません。

Taskこの問題が何であるかについて間違った印象を与える可能性があるため、回答で紹介することはまだ避けています。人々は を理解するのに苦労Taskしており、BCL では比較的新しいものです。


この答えは大きく進化しました。最終的に、これはスレッドFunc<T>Func<IEnumerable<T>>にあるのと同じ根本的な問題であることが判明しまし. Func<int>andを使用した私の例は、Func<FileStream>ほぼ同じように明確です。Eric Lippert は、別のスレッドで適切な回答を提供しています。

于 2013-11-15T20:53:41.120 に答える
9

これは .Net 4.0 で修正されるはずでしたが、Task.Run() は .Net 4.5 の新機能です。

.NET 4.5 には、メソッドを追加することにより、独自のオーバーロードのあいまいさがありTask.Run(Func<Task<T>>)ます。また、C# バージョン 5 での async/await のサポート。これにより、 からT foo()への暗黙的な変換が可能になりFunc<Task<T>>ます。

これは、async/await にはかなり適したシンタックス シュガーですが、ここで空洞が発生します。asyncメソッド宣言でのキーワードの省略は、メソッドのオーバーロードの選択では考慮されません。これにより、プログラマーが意図したときにasyncを使用することを忘れるという別の不幸のパンドラボックスが開かれます。それ以外の場合は、メソッド シグネチャ内のメソッド名と引数のみがメソッド オーバーロードの選択対象と見なされるという通常の C# 規則に従います。

あいまいさを解決するには、デリゲート型を明示的に使用する必要があります。

于 2013-11-08T23:38:57.537 に答える
1

過負荷解決の問題のようです。コンパイラは、呼び出しているオーバーロードを認識できません (最初に、作成する正しいデリゲートを見つける必要がありますが、呼び出しているオーバーロードに依存するため、コンパイラにはわかりません)。推測して確認する必要がありますが、それほどスマートではないと思います。

于 2012-07-27T21:31:24.230 に答える
-1

ここに私の刺し傷があります:

public class MyTest
{
    public void RunTest()
    {
        Task<Int32> t = Task.Run<Int32>(new Func<int>(MyIntReturningMethod));
        t.Wait();
        Console.WriteLine(t.Result);
    }

    public int MyIntReturningMethod()
    {
        return (5);
    }
}
于 2013-11-14T21:36:55.730 に答える