6

この質問は、関連する投稿の説明になります。以下の例は、問題の本質を説明していると思います。

class Program
{
    public static IList<string> GetData(string arg)
    {
        return new string[] {"a", "b", "c"};
    }

    static void Main(string[] args)
    {
        var arg1 = "abc";
        var res1 = GetData(arg1);
        Console.WriteLine(res1.Count());

        dynamic arg2 = "abc";
        var res2 = GetData(arg2);
        try
        {
            Console.WriteLine(res2.Count());
        }
        catch (RuntimeBinderException)
        {
            Console.WriteLine("Exception when accessing Count method");
        }

        IEnumerable<string> res3 = res2;
        Console.WriteLine(res3.Count());
    }
}

GetDataが動的にキャストされたパラメーターを受け取ったという理由だけでGetDataへの2番目の呼び出しが例外を発生させるのは悪いことではありませんか?メソッド自体はそのような引数で問題ありません。それは文字列として扱い、正しい結果を返します。しかし、結果は再び動的にキャストされ、突然、結果データはその基になるタイプに従って処理できなくなります。例の最後の行にあるように、静的型に明示的にキャストバックされない限り。

なぜこのように実装する必要があるのか​​理解できませんでした。静的タイプと動的タイプの間の相互運用性を壊します。ダイナミックが使用されると、コールチェーンの残りの部分に感染し、このような問題を引き起こす可能性があります。

更新。Count()は拡張メソッドであり、認識されないことは理にかなっていると指摘する人もいます。次に、res2.Count()の呼び出しをres2.Countに変更しました(拡張メソッドからIlistのプロパティに)が、プログラムは同じ場所で同じ例外を発生させました!今それは奇妙です。

UPDATE2。flqは、このトピックに関するEric Lippertのブログ投稿を指摘しました。この投稿は、このように実装されている理由を十分に説明していると思います。http: //blogs.msdn.com/b/ericlippert/archive/2012/10/22/a -method-group-of-one.aspx

4

1 に答える 1

7

問題は、Countが拡張メソッドであるということです。

実行時に拡張メソッドをどのように見つけますか?「スコープ内」にある情報は、コンパイルされる特定のファイルの「using」ステートメントに基づいています。ただし、これらは実行時にコンパイルされたコードには含まれていません。ロードされたすべてのアセンブリで可能なすべての拡張メソッドを調べる必要がありますか?プロジェクトによって参照されたが、まだロードされていないアセンブリはどうですか?拡張メソッドの動的な使用を許可しようとすると、驚くほど多くの境界ケースが発生します。

この場合の正しい解決策は、静的メソッドを非拡張形式で呼び出すことです。

Enumerable.Count(res2)

または、この場合はわかっているのでIList<T>、Countプロパティを使用します。

res2.Count<---編集:これは、配列によって実装されたときに明示的に実装されたインターフェイスプロパティであるため、機能しません。


あなたの質問をもう一度見てみると、本当の質問は拡張メソッドの解決自体ではなく、単一のメソッドの解決が可能であると判断できないため、静的にタイプを知っている理由です。もう少し考えなければなりませんが、特に複数のオーバーロードを検討し始めたら、それは境界の場合の同様の質問だと思います。


これは、一般的なケースで発生する可能性のある1つの厄介な境界ケースです(ただし、Objectから派生しているため、ケースに直接適用することはできません)。

アセンブリAにクラスBaseがあるとします。アセンブリBにもクラスDerived:Baseがあります。Derivedクラスには、上からのコードがあり、GetDataの可能な解決策は1つだけだと思います。ただし、ここで、異なる署名を持つ保護されたGetDataメソッドを持つ新しいバージョンのアセンブリAが公開されたとします。派生クラスはこれを継承し、DLRはこの新しいメソッドへの動的バインディングを忠実に許可します。突然、リターンタイプが想定したものとは異なる場合があります。これはすべて、アセンブリBを再コンパイルしなくても発生する可能性があることに注意してください。これは、実行時の動的環境が異なるタイプを生成する可能性があるため、実行前コンパイラがDLRが実行前コンパイラが唯一のオプションであると信じるタイプに解決されると想定できないことを意味します。

于 2012-11-14T21:47:06.857 に答える