110

Foo()次のコードには、インスタンス メソッド を呼び出す静的メソッド がありBar()ます。

public sealed class Example
{
    int count;

    public static void Foo( dynamic x )
    {
        Bar(x);
    }

    void Bar( dynamic x )
    {
        count++;
    }
}

エラーなしでコンパイルされます*が、実行時にランタイムバインダー例外が生成されます。これらのメソッドの動的パラメーターを削除すると、予想どおりコンパイラ エラーが発生します。

では、動的パラメーターを使用すると、コードをコンパイルできるのはなぜでしょうか? ReSharper もエラーとして表示しません。

編集 1: * Visual Studio 2008 で

編集 2:sealedサブクラスに静的メソッドが含まれる可能性があるため、追加されましたBar(...)。インスタンス メソッド以外のメソッドを実行時に呼び出すことができない場合は、封印されたバージョンでもコンパイルされます。

4

3 に答える 3

71

更新: 以下の回答は、C# 7.3 (2018 年 5 月) の導入前の2012 年に書かれました。What's new in C# 7.3セクションの改良されたオーバーロード候補の項目 1 では、非静的オーバーロードが早期に破棄されるようにオーバーロード解決規則がどのように変更されたかが説明されています。したがって、以下の回答 (およびこの質問全体) は、今のところほとんど歴史的な関心しかありません!


(C# 7.3より前:)

なんらかの理由で、オーバーロードの解決は、静的と非静的をチェックする前に常に最適な一致を見つけます。すべての静的タイプでこのコードを試してください:

class SillyStuff
{
  static void SameName(object o) { }
  void SameName(string s) { }

  public static void Test()
  {
    SameName("Hi mom");
  }
}

最適なオーバーロードはstring. しかし、これはインスタンス メソッドであるため、コンパイラは (2 番目に優れたオーバーロードを使用する代わりに) 文句を言います。

追加:したがってdynamic、元の質問の例の説明は、一貫性を保つために、型が動的な場合、最初に最適なオーバーロードも見つけることだと思います(静的と非ではなく、パラメーター番号とパラメーター型などのみをチェックします) -static)、その後にのみ静的をチェックします。ただし、これは静的チェックが実行時まで待たなければならないことを意味します。したがって、観察された動作。

後半の追加: なぜ彼らがこのおかしな順序で物事を行うことを選択したのかについての背景は、Eric Lippert によるこのブログ投稿から推測できます。

于 2012-10-11T16:00:08.200 に答える
30

Foo には、動的なパラメーター "x" があります。これは、Bar(x) が動的な式であることを意味します。

Example に次のようなメソッドを含めることは完全に可能です。

static Bar(SomeType obj)

その場合、正しいメソッドが解決されるため、ステートメント Bar(x) は完全に有効です。インスタンス メソッド Bar(x) が存在するという事実は無関係であり、考慮さえされていません。定義により、Bar(x) は動的な式であるため、実行時に解決を延期しました。

于 2012-10-11T15:17:23.147 に答える
9

「動的」式は実行時にバインドされるため、正しいシグネチャまたはインスタンス メソッドで静的メソッドを定義すると、コンパイラはそれをチェックしません。

「正しい」方法は実行時に決定されます。コンパイラは、実行時に有効なメソッドがあるかどうかを知ることができません。

"dynamic" キーワードは動的およびスクリプト言語用に定義されており、メソッドは実行時であってもいつでも定義できます。バカバカしく

メソッドがインスタンス上にあるため、int を処理するが文字列を処理しないサンプルを次に示します。

class Program {
    static void Main(string[] args) {
        Example.Foo(1234);
        Example.Foo("1234");
    }
}
public class Example {
    int count;

    public static void Foo(dynamic x) {
        Bar(x);
    }

    public static void Bar(int a) {
        Console.WriteLine(a);
    }

    void Bar(dynamic x) {
        count++;
    }
}

処理できなかったすべての「間違った」呼び出しを処理するメソッドを追加できます

public class Example {
    int count;

    public static void Foo(dynamic x) {
        Bar(x);
    }

    public static void Bar<T>(T a) {
        Console.WriteLine("Error handling:" + a);
    }

    public static void Bar(int a) {
        Console.WriteLine(a);
    }

    void Bar(dynamic x) {
        count++;
    }
}
于 2012-10-11T15:14:05.270 に答える