7

Id プロパティだけを持つクラスから派生したクラスがいくつかありBaseClassます。BaseClass

これらのオブジェクトのいくつかのコレクションを区別する必要があります。子クラスごとに次のコードを何度も使用しています。

public class PositionComparer : IEqualityComparer<Position>
{
    public bool Equals(Position x, Position y)
    {
        return (x.Id == y.Id);
    }

    public int GetHashCode(Position obj)
    {
        return obj.Id.GetHashCode();
    }
}

ロジックが単に に基づいていることを考えると、Id重複を減らすために単一の比較子を作成したかったのです。

public class BaseClassComparer : IEqualityComparer<BaseClass>
{
    public bool Equals(BaseClass x, BaseClass y)
    {
        return (x.Id == y.Id);
    }

    public int GetHashCode(BaseClass obj)
    {
        return obj.Id.GetHashCode();
    }
}

しかし、これはコンパイルされていないようです:

  IEnumerable<Position> positions = GetAllPositions();
  positions = allPositions.Distinct(new BaseClassComparer())

BaseClass... からに変換できないと書かれていますPositionDistinct()比較子がこの呼び出しの戻り値を強制するのはなぜですか?

4

5 に答える 5

7

Distinctの定義を見ると、関与するジェネリック型パラメーターは 1 つだけです (入力コレクションと出力コレクションに使用される TCollection は 1 つではなく、比較子には TComparison が 1 つ使用されません)。つまり、 BaseClassComparer が結果の型を基本クラスに制限し、代入での変換が不可能であることを意味します。

少なくとも基本クラスになるように制約されたジェネリック パラメータを使用して GenericComparer を作成すると、実行しようとしていることに近づく可能性があります。これは次のようになります

public class GenericComparer<T> : IEqualityComparer<T> where T : BaseClass
{
    public bool Equals(T x, T y)
    {
        return x.Id == y.Id;
    }

    public int GetHashCode(T obj)
    {
        return obj.Id.GetHashCode();
    }
}

メソッド呼び出しだけでなくインスタンスが必要なため、ジェネリック型をコンパイラーに推論させることはできませんが (このディスカッションを参照)、インスタンスを作成するときにそうする必要があります。

IEnumerable<Position> positions;
positions = allPositions.Distinct(new GenericComparer<Position>());

エリックの答えは、問題全体の根本原因を説明しています(共分散と反分散の観点から)。

于 2013-04-06T13:18:21.353 に答える
1

あなたが持っていたと想像してください:

var positions = allPositions.Distinct(new BaseClassComparer());

タイプは何だと思いますpositionsか?Distinctコンパイラは、どの実装に与えられた引数から推測するIEqualityComparer<BaseClass>ので、式の型は ですIEnumerable<BaseClass>

その型は自動的に変換できないIEnumerable<Position>ため、コンパイラはエラーを生成します。

于 2013-04-06T13:20:53.787 に答える
0

IEqualityComparer<T>は type で反変であるためT、ジェネリック パラメータを に指定すると、基本クラスの比較子を distinct で使用できますDistinct

IEnumerable<Position> distinct = positions.Distinct<Position>(new BaseClassComparer());

これを指定しない場合、コンパイラはの型がTimplements以降BaseClassであると推測します。BaseClassComparerIEqualityComparer<BaseClass>

于 2013-04-06T13:41:54.053 に答える
0

コードに小さな変更が必要です。以下の作業例:

public class BaseClass
{
    public int Id{get;set;}
}

public class Position : BaseClass
{
    public string Name {get;set;}
}
public class Comaprer<T> : IEqualityComparer<T>
    where T:BaseClass
{

    public bool Equals(T x, T y)
    {
        return (x.Id == y.Id);
    }

    public int GetHashCode(T obj)
    {
        return obj.Id.GetHashCode();
    }
}
class Program
{
    static void Main(string[] args)
    {
        List<Position> all = new List<Position> { new Position { Id = 1, Name = "name 1" }, new Position { Id = 2, Name = "name 2" }, new Position { Id = 1, Name = "also 1" } };
        var distinct = all.Distinct(new Comaprer<Position>());

        foreach(var d in distinct)
        {
            Console.WriteLine(d.Name);
        }
        Console.ReadKey();
    }
}
于 2013-04-06T13:46:07.747 に答える