Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在IComparer&;IEqualityComparer接口_C#_C# 4.0_Contravariance - Fatal编程技术网

C# 在IComparer&;IEqualityComparer接口

C# 在IComparer&;IEqualityComparer接口,c#,c#-4.0,contravariance,C#,C# 4.0,Contravariance,关于逆变,我发现了一个非常有趣的例子,展示了“IComparer逆变的好处” 首先,它们使用相当奇怪的基类和派生类: public class Person { public string FirstName { get; set; } public string LastName { get; set; } } public class Employee : Person { } 我已经可以说,这是一个很糟糕的例子,因为没有一个类只继承基类而不添加至少一点自己的东西 然后创

关于逆变,我发现了一个非常有趣的例子,展示了“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)
    {
       ..
    }
}
class PersonComparer:IEqualityComparer
{
公共布尔等于(人x,人y)
{            
..
}
public int GetHashCode(个人)
{
..
}
}
下一个有问题的例子是

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());
List employees=新列表{
新员工(){FirstName=“Michael”,LastName=“Alexander”},
新员工(){FirstName=“Jeff”,LastName=“Price”}
};
IEnumerable节点副本=
employees.Distinct(newpersoncomparer());
现在我的问题是——首先,在这种情况下,Employee是一个不必要的类,它确实可以使用PersonComparer来处理这种情况,因为它实际上只是一个person类

然而,在现实世界中,
Employee
至少会有一个新字段,比如说一个
JobTitle
。很明显,当我们想要区分员工时,我们需要记住JobTitle字段进行比较,并且很明显,person Comparer之类的逆变比较器不适合该工作,因为它无法知道员工定义的任何新成员

当然,任何语言特性,即使是一个非常奇怪的特性,都可能有它的用途,即使它在某些情况下是不合逻辑的,但在这种情况下,我认为作为默认行为,它不会太有用。事实上,在我看来,当我们稍微破坏了类型安全性时,当一个方法需要一个雇员比较器时,我们实际上可以放入一个person甚至object比较器,它将毫无问题地编译。很难想象我们的默认场景是将员工视为对象……或基本的人

那么,对于这些接口来说,这真的是一个很好的默认值对比吗


编辑:我理解什么是逆变和协方差。我在问为什么这些比较接口在默认情况下会被更改为反变的。

这个问题更像是一个咆哮,但让我们先回顾一下,谈谈比较器

当您需要覆盖对象可用的任何默认相等比较器时,
IEqualityComparer
非常有用。它可以使用自己的相等逻辑(覆盖Equals和GetHashCode),也可以使用默认的引用相等,等等。关键是你不想要它的默认值
IEqualityComparer
允许您精确指定您希望用于实现平等的内容。它可以让你定义尽可能多的方法来解决你可能遇到的各种问题

许多不同问题中的一个可能恰好可以通过一个比较器来解决,这个比较器已经存在于一个较小的派生类型中。这就是这里发生的一切,你有能力提供比较器来解决你需要解决的问题。您可以使用更通用的比较器,同时拥有更派生的集合


在这个问题中,您的意思是“仅对基本属性进行比较是可以的,但将较小的派生对象(或同级对象)放入集合是不可以的。”

逆变的定义如下。如果只要
U
V
是类型,那么从类型到类型的映射
T
F
T
中是逆变的,那么
U
类型的每个对象都可以被分配到
V
类型的变量,可以将
F
类型的每个对象分配给
F
类型的变量(
F
反向分配兼容性)

特别是,如果
T->IComparer
,请注意类型为
IComparer
的变量可以接收实现
IComparer
的对象。这是相反的

我们之所以说,
IComparer
T
中是反变的,是因为你可以说

class SomeAnimalComparer  : IComparer<Animal> { // details elided }
然后你可以说

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

IComparer<U> someUComparer = new SomeUComparer();
IComparer<V> vComparer = 
    new ContravarianceWrapperForIComparer<U, V>(someUComparer);
class-SomeUComparer:IComparer{//details-elided}
IComparer someUComparer=新的someUComparer();
I比较者V比较者=
为iComparer(someUComparer)提供新的逆变包装;
相反,你可以跳过这些咒语,只说

IComparer<V> vComparer = someUComparer;
IComparer vComparer=someUComparer;

当然,上述情况仅在
V:U
时发生。只要
U
V

员工是个人,只要
U
的分配与
V
相兼容,您就可以使用相反的方法执行此操作。因为比较器需要一个超类Person,所以只要这些元素扩展Person,就可以对它们进行比较。其思想是,人的价值应该始终与人的价值相比较。这是一个很好的例子:

如果我们有Person.ssn,那么应该在.equals()方法中对其进行比较。因为我们比较了ssn,确保每个人的ssn都是唯一的;那么,员工是经理还是炒菜厨师并不重要,因为我们已经确保他们是一样的

现在,如果有,可以有多个具有相同SSN和不同员工属性的人员;那么,你应该考虑不要让人成为逆反型,让员工成为最容易接受的类型。 相反有助于对接口编程,而不必了解接口的所有可能实现。当然,这允许更好的扩展性和创建接口的新实例来扩展程序的功能

逆变数组类型还可以帮助我们创建嵌套类,这些嵌套类扩展接口并将其传递回去。例如,如果我们
 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;