c# – 矛盾解释

首先,我已经阅读了关于协议和逆向的SO和博客的许多解释,并大力感谢Eric LippertCovariance and Contravariance生产这样伟大的系列。

但我有一个更具体的问题,我试图让我的头一点点。

根据我的理解,根据Eric’s explanation是协方差和矛盾是描述转型的形容词。协变换是保留类型的顺序,而变换变换是逆转它的。

我理解协方差这种方式,我认为大多数开发人员直观地理解。

//covariant operation
Animal someAnimal = new Giraffe(); 
//assume returns Mammal, also covariant operation
someAnimal = Mammal.GetSomeMammal(); 

这里的返回操作是协变的,因为我们保留两个动物仍然大于哺乳动物或长颈鹿的大小。在这一点上,大多数返回操作是协变的,逆变操作是没有意义的。

  //if return operations were contravariant
  //the following would be illegal
  //as Mammal would need to be stored in something
  //equal to or less derived than Mammal
  //which would mean that Animal is now less than or equal than Mammal
  //therefore reversing the relationship
  Animal someAnimal =  Mammal.GetSomeMammal(); 

这段代码当然对大多数开发人员没有意义。

我的困惑在于Contravariant参数参数。如果你有一个方法,如

bool Compare(Mammal mammal1, Mammal mammal2);

我总是知道输入参数总是强制逆变器的行为。这样,如果类型用作输入参数,其行为应该是逆变的。

但是下面的代码有什么区别

Mammal mammal1 = new Giraffe(); //covariant
Mammal mammal2 = new Dolphin(); //covariant

Compare(mammal1, mammal2); //covariant or contravariant?
//or
Compare(new Giraffe(), new Dolphin()); //covariant or contravariant?

同样的道理,你不能做这样的事你不能做

   //not valid
   Mammal mammal1 = new Animal();

   //not valid
   Compare(new Animal(), new Dolphin());

我想我的问题是,什么使方法参数传递一个逆变换。

对不起,长的职位,也许我明白这不正确。

编辑:

根据下面的一些谈话,我理解,例如使用委托层可以清楚地显示逆向。请考虑以下示例

//legal, covariance
Mammal someMammal = new Mammal();
Animal someAnimal = someMammal;

// legal in C# 4.0, covariance (because defined in Interface)
IEnumerable<Mammal> mammalList = Enumerable.Empty<Mammal>();
IEnumerable<Animal> animalList = mammalList;

//because of this, one would assume
//that the following line is legal as well

void ProcessMammal(Mammal someMammal);

Action<Mammal> processMethod = ProcessMammal;
Action<Animal> someAction = processMethod;

当然这是非法的,因为有人可以传递任何动物到someAction,在那里作为ProcessMammal期望任何东西是哺乳动物或更具体(小于哺乳动物)。这就是为什么someAction只能是Action或任何更具体的(Action)

然而,这是在中间引入一层代表,是否有必要为了逆变的投影发生,在中间有一个委托?如果我们定义Process作为一个接口,我们将声明参数参数作为一个逆变类型只是因为我们不希望有人能够做我上面显示的代理人?

public interface IProcess<out T>
{
    void Process(T val);
}
更新:Ooops。事实证明,我混淆了方差和“任务兼容性”在我的初始答案。相应地编辑了答案。还有我写了一篇博客文章,我希望更好地回答这样的问题:Covariance and Contravariance FAQ

答案:我想你的第一个问题的答案是,你没有在这个例子中的逆向:

bool Compare(Mammal mammal1, Mammal mammal2); 
Mammal mammal1 = new Giraffe(); //covariant - no             
Mammal mammal2 = new Dolphin(); //covariant - no            

Compare(mammal1, mammal2); //covariant or contravariant? - neither            
//or             
Compare(new Giraffe(), new Dolphin()); //covariant or contravariant? - neither

此外,你甚至没有协方差在这里。您所拥有的称为“赋值兼容性”,这意味着您可以始终将较多派生类型的实例分配给较少派生类型的实例。

在C#中,数组,委托和通用接口支持方差。正如Eric Lippert在他的博客文章What’s the difference between covariance and assignment compatibility?中所说,最好将方差考虑为类型的“投影”。

协方差更容易理解,因为它遵循赋值兼容性规则(更多派生类型的数组可以分配给较少派生类型的数组,“object [] objs = new string [10];”)。矛盾反转了这些规则。例如,想象你可以做一些类似“string [] strings = new object [10];”。当然,你不能这样做,因为明显的原因。但是,这将是逆变(但是,再次,数组不是逆变的,他们只支持协方差)。

这里是从MSDN的例子,我希望会告诉你什么反向真的意味着(我现在拥有这些文件,所以如果你认为文件中的东西不清楚,随时给我反馈):

> Using Variance in Interfaces for Generic Collections

Employee[] employees = new Employee[3];
// You can pass PersonComparer, 
// which implements IEqualityComparer<Person>,
// although the method expects IEqualityComparer<Employee>.
IEnumerable<Employee> noduplicates =
    employees.Distinct<Employee>(new PersonComparer());

> Using Variance in Delegates

// Event hander that accepts a parameter of the EventArgs type.
private void MultiHandler(object sender, System.EventArgs e)
{
   label1.Text = System.DateTime.Now.ToString();
}
public Form1()
{
    InitializeComponent();
    // You can use a method that has an EventArgs parameter,
    // although the event expects the KeyEventArgs parameter.
    this.button1.KeyDown += this.MultiHandler;
    // You can use the same method 
    // for an event that expects the MouseEventArgs parameter.
    this.button1.MouseClick += this.MultiHandler;
 }

> Using Variance for Func and Action Generic Delegates

 static void AddToContacts(Person person)
 {
   // This method adds a Person object
   // to a contact list.
 }

 // The Action delegate expects 
 // a method that has an Employee parameter,
 // but you can assign it a method that has a Person parameter
 // because Employee derives from Person.
 Action<Employee> addEmployeeToContacts = AddToContacts;

希望这可以帮助。

http://stackoverflow.com/questions/1962629/contravariance-explained

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c# – 矛盾解释