5

反変性に関するmsdnページで、「IComparerでの反変性の利点」を示す非常に興味深い例を見つけました

まず、かなり奇妙な基本クラスと派生クラスを使用します。

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Employee : Person { }

私はすでに、その悪い例は、クラスが少なくとも独自のものを少し追加せずに基本クラスを継承することは決してないと言うことができます.

次に、単純な IEqualityComparer クラスを作成します。

class PersonComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {            
        ..
    }
    public int GetHashCode(Person person)
    {
       ..
    }
}

次に、問題の例が続きます。

List<Employee> employees = new List<Employee> {
               new Employee() {FirstName = "Michael", LastName = "Alexander"},
               new Employee() {FirstName = "Jeff", LastName = "Price"}
            };

IEnumerable<Employee> noduplicates = 
employees.Distinct<Employee>(new PersonComparer());

今私の質問 - まず第一に、この場合 Employee は不要なクラスです。実際には単なる人物クラスであるため、この状況で PersonComparer を使用できるのは事実です!

ただし、現実の世界Employeeでは、少なくとも 1 つの新しいフィールドがありますJobTitle。異なる従業員が必要な場合、比較のためにその JobTitle フィールドを考慮する必要があることは明らかであり、人の比較者などの反変比較者はその仕事に適していないことは明らかであるため、新しいメンバーを知ることができないためです。従業員が定義しました。

もちろん、非常に奇妙な機能であっても、状況によっては非論理的であっても、どの言語機能でも使用できますが、この場合、デフォルトの動作であるほど頻繁には役に立たないと思います。実際、型の安全性を少し壊しているように見えます。メソッドが Employee 比較子を期待している場合、実際には人またはオブジェクト比較子を入れることができ、問題なくコンパイルされます。デフォルトのシナリオが Employee をオブジェクトまたは基本的な Person のように扱うことになるとは想像しがたいですが。

それで、それらのインターフェースのデフォルトの反変性は本当に良いのでしょうか?

編集:反分散と共分散とは何かを理解しています。これらの比較インターフェースがデフォルトで反変になるように変更された理由を尋ねています。

4

3 に答える 3

6

反変の定義は次のとおりです。F型から型へのマッピングTは、型のすべてのオブジェクトを型の変数に割り当てることができるような型である場合はいつでもF<T>反変です。TUVUVF<V>F<U>F

特に、 if then 型の変数は を実装するオブジェクトを受け取ることができることT -> IComparer<T>に注意してください。これが反変性です。IComparer<Derived>IComparer<Base>

IComparer<T>それが反変であると私たちが言う理由Tは、あなたが言うことができるからです

class SomeAnimalComparer  : IComparer<Animal> { // details elided }

その後:

IComparer<Cat> catComparer = new SomeAnimalComparer();

編集:あなたは言う:

反分散と共分散が何であるかを理解しています。これらの比較インターフェースがデフォルトで反変になるように変更された理由を尋ねています。

かわった?つまり、IComparer<T>「自然に」反変です。の定義IComparer<T>は次のとおりです。

 public interface IComparer<T> {
     int Compare(T x, T y);
 }

Tこのインターフェイスでは「in」の位置にのみ表示されることに注意してください。つまり、 のインスタンスを返すメソッドはありませんTそのようなインターフェースは、 では「自然に」反変ですT

これを考えると、反変にしたくない理由は何ですか? Uのインスタンスを比較する方法を知っていて、V代入が と互換性のあるオブジェクトを持っている場合U、このオブジェクトを のインスタンスを比較する方法を知っているものと考えることができないのはなぜVですか? これは、反変性が許すものです。

反変性の前に、ラップする必要があります:

 class ContravarianceWrapperForIComparer<U, V> : IComparer<V> where V : U {
      private readonly IComparer<U> comparer;
      public ContravarianceWrapperForIComparer(IComparer<U> comparer) {
          this.comparer = comparer;
      }
      public int Compare(V x, V y) { return this.comparer.Compare(x, y); }
 }

そして、あなたは言うことができます

class SomeUComparer : IComparer<U> { // details elided }

IComparer<U> someUComparer = new SomeUComparer();
IComparer<V> vComparer = 
    new ContravarianceWrapperForIComparer<U, V>(someUComparer);

反変性により、これらの呪文をスキップして、ただ言うことができます

IComparer<V> vComparer = someUComparer;

もちろん、上記はV : U. U反変性を使用すると、 からの代入と互換性があるときはいつでもそれを行うことができますV

于 2011-06-22T02:34:42.533 に答える
4

この質問は暴言のように聞こえますが、少し戻って比較子について話しましょう。

これIEqualityComparer<T>は、オブジェクトで使用できるデフォルトの等値比較子をオーバーライドする必要がある場合に便利です。独自の等価ロジック (Equals と GetHashCode をオーバーライド) を使用している可能性もあれば、デフォルトの参照等価性を使用している可能性もあります。ポイントは、デフォルトが何であれ、必要ないということです。 IEqualityComparer<T>を使用すると、平等に使用するものを正確に指定できます。また、さまざまな問題を解決するために必要なだけさまざまな方法を定義できます。

これらの多くの異なる問題の 1 つは、下位の派生型に対して既に存在する比較子によってたまたま解決できる場合があります。ここで起こっていることはそれだけです。解決する必要がある問題を解決する比較子を提供することができます。より派生したコレクションを持ちながら、より一般的な比較子を使用できます。

この問題では、「基本プロパティのみを比較するのは問題ありませんが、下位の派生オブジェクト (または兄弟) をコレクションに入れるのは問題です」と言っています。

于 2011-06-22T02:33:45.003 に答える
3

従業員は人です。コンパレータにはスーパークラスのPersonが必要なため、Personを拡張する限り、これらの要素を比較できるようにします。アイデアは、人は常に人に匹敵する必要があるということです。これが当てはまる理由の完璧な例は次のとおりです。

Person.ssnがある場合は、.equals()メソッドで比較する必要があります。ssnを比較し、ssnがすべての人に固有であることが保証されているためです。その場合、従業員がマネージャーであるかフライクックであるかは関係ありません。これは、従業員が同じであることを確認しているためです。

ここで、同じSSNと異なる従業員属性を持つ複数の個人を持つことができる場合。次に、Personを反変型にせず、Employeeを最も広い型に受け入れられるようにすることを検討する必要があります。

共変性は、インターフェースにプログラムするのに役立ち、インターフェースのすべての可能な実装を知っているわけではありません。もちろん、これにより、拡張性が向上し、インターフェイスの新しいインスタンス化を作成して、プログラムの機能を拡張できます。

反変配列タイプは、インターフェイスを拡張してそれらを返すネストされたクラスを作成するのにも役立ちます。たとえば、従業員の配列を作成し、必ずしも従業員を世界の解像度に公開したくない場合。一連のPersonを送り返すことができます。共変性のため、実際にはEmployeeの配列をPersonの配列として送り返すことができます。

于 2011-06-22T02:35:55.687 に答える