7

タイプの変数がある場合、名前空間内で利用可能なIQueryable<T>4 つの拡張メソッドがあります。WhereSystm.Linq

public static IQueryable<T> Where<T>(this IQueryable<T> source,
    Expression<Func<T, bool>> predicate);
public static IQueryable<T> Where<T>(this IQueryable<T> source,
    Expression<Func<T, int, bool>> predicate);
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
    Func<T, bool> predicate);
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
    Func<T, int, bool> predicate);

(最後の 2 つは からIQueryable<T>継承するためIEnumerable<T>です。)

ObjectQuery<T>( namespaceで)型の変数を使用する場合、利用可能なSystem.Data.Objects5 つのオーバーロードがWhereあります。つまり、上記の 4 つ (ObjectQuery<T>実装IQueryable<T>およびIEnumerable<T>他のインターフェイスの中で) に加えて、このクラスのインスタンス メソッドがあります。

public ObjectQuery<T> Where(string predicate,
    params ObjectParameter[] parameters);

IQueryable<T>どちらかを使用しているときに同じプログラミングの間違いを犯した場合、またはObjectQuery<T>非常に異なるコンパイラ エラーが発生します。サンプル プログラムを次に示します (VS2010 SP1 の標準 C# コンソール アプリケーション テンプレート +System.Data.Entity.dllプロジェクト参照に追加されたアセンブリ、4 つの例の下のコメントにコンパイラ エラーがあります)。

using System.Data.Objects;
using System.Linq;

namespace OverloadTest
{
    public class Test
    {
        public int Id { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IQueryable<Test> queryable = null;
            ObjectQuery<Test> objectQuery = null;

            var query1 = queryable.Where(t => t.Name == "XYZ");
            // no definition for "Name" in class OverloadTest.Test

            var query2 = queryable.Where(t => bla == blabla);
            // "bla", "blabla" do not exist in current context

            var query3 = objectQuery.Where(t => t.Name == "XYZ");
            // Delegate System.Func<Overload.Test,int,bool>
            // does not take 1 argument

            var query4 = objectQuery.Where(t => bla == blabla);
            // Delegate System.Func<Overload.Test,int,bool> 
            // does not take 1 argument
        }
    }
}

コンパイラでは、「波線」も異なって見えます。

ここに画像の説明を入力

最初の 2 つのエラーは理解しています。しかし、なぜコンパイラはFunc<T, int, bool> predicate、最後の 2 つの例でオーバーロード番号 4 (を使用) を使用したいのか、「Name」がクラスTestで定義されておらず、「bla」と「blabla」が存在しないことを教えてくれないのはなぜですか。現在の文脈では?

私は、コンパイラがオーバーロード番号 5 (as パラメーターを渡さない) とオーバーロード番号 2 と 4 ( 2 つのパラメーターstringを持つラムダ式を渡さない) を安全に除外できると予想していましたが、私の期待はそうではありません。正しいようです。(t,i) => ...

補足として、この質問を見たときにこの問題に遭遇しました。質問者は、質問の 4 番目のクエリがコンパイルされない (上記の例 3 と 4 のコンパイラ エラーとまったく同じ) と言いましたが、このクエリはまさに彼の問題の解決策であり、私には何か (変数またはプロパティ名?) がクエリに間違って記述されています (ただし、彼はこれを確認していません) が、このコンパイラ エラーは、何が間違っているかを示すのに役立ちません。

編集

以下のMartin Harrisの非常に役立つコメントを参照してください。

query4では、「Delegate System.Func does not take 1 argument」というエラーは、波線の上にカーソルを置いたときにツールヒント ウィンドウに表示されるエラーです。コンパイラの出力ウィンドウには、実際には次の順序で 4 つのエラーが表示されます。

  • デリゲート System.Func は引数を 1 つ取りません
  • 「文字列」はデリゲート型ではないため、「ラムダ式」を「文字列」に変換できません
  • 名前「bla」は現在のコンテキストには存在しません
  • 名前「blabla」は現在のコンテキストには存在しません

しかし、コンパイラが を使用する最初の 2 つの例で最初のエラーを出さないのはなぜIQueryable<T>でしょうか?

4

1 に答える 1

4

最後までお読みください。

実際には、コードにコンパイラ時間エラーがあるためです。

コンパイラは、コードを見て正しい拡張メソッドを検出します。この場合、パラメーターを受け取り、Testパラメーターを返すことになっていboolます。linq 式をコンパイルできないため、正しい拡張メソッドを検出できず、コンパイラは最初に見つかった拡張メソッドが必要な拡張メソッドであると想定します。

ところで、次のようなエラーを修正すると

var query3 = objectQuery.Where(t => t.Id == 1)

コンパイラが使用します

public static IQueryable<T> Where<T>(
       this IQueryable<T> source,
       Expression<Func<T, bool>> predicate
);

なぜ Enumerable のメソッドをスキップするのか不思議に思うはずです。ObjectQuery<T>クラスが「IQueryable」を直接実装しているためIEnumerable<T>ですが、IQueryable<T>.

以下のオブジェクト階層を見ることができます
オブジェクト階層

于 2012-08-02T15:09:13.210 に答える