gpt4 book ai didi

c# - 逆变解释

转载 作者:IT王子 更新时间:2023-10-29 04:02:30 26 4
gpt4 key购买 nike

首先,我阅读了很多关于 SO 的解释以及关于协变和逆变的博客,非常感谢 Eric LippertCovariance and Contravariance 上制作了如此出色的系列.

不过,我有一个更具体的问题,我想稍微了解一下。

据我了解 Eric's explanation是协变和逆变都是描述转换的形容词。协变变换是保留类型顺序的变换,而逆变变换是反转它的变换。

我认为大多数开发人员都可以凭直觉理解协方差。

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

这里的返回操作是协变的,因为我们保留了 Animal 仍然大于 Mammal 或 Giraffe 的大小。关于这一点,大多数返回操作都是协变的,逆变操作没有意义。

  //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();

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

我的困惑在于逆变参数参数。如果你有这样的方法

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());

我想我想问的是,是什么让方法参数通过逆变转换。

抱歉,这篇文章很长,可能是我理解有误。

编辑:

根据下面的一些对话,我了解到例如使用委托(delegate)层可以清楚地显示逆变。考虑以下示例

//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;

当然这是非法的,因为有人可以将任何 Animal 传递给 someAction,而 ProcessMammal 期望任何 Mammal 或更具体(小于 Mammal)的东西。这就是为什么 someAction 只能是 Action 或任何更具体的 (Action)

然而,这在中间引入了一层委托(delegate),是否必须在中间有一个委托(delegate)才能发生逆变投影?如果我们将 Process 定义为接口(interface),我们会将参数参数声明为逆变类型只是因为我们不希望有人能够使用委托(delegate)执行我上面显示的操作?

public interface IProcess<out T>
{
void Process(T val);
}

最佳答案

更新:糟糕。事实证明,我在最初的回答中混淆了方差和“分配兼容性”。相应地编辑了答案。我还写了一篇博文,希望能更好地回答这些问题: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# 中,数组、委托(delegate)和通用接口(interface)支持变体。正如 Eric Lippert 在他的博客文章中所说 What's the difference between covariance and assignment compatibility?是最好将方差视为类型的“投影”。

协变更容易理解,因为它遵循赋值兼容性规则(派生类型多的数组可以赋值给派生类型少的数组,“object[] objs = new string[10];”)。逆变颠倒了这些规则。例如,假设您可以执行类似“string[] strings = new object[10];”的操作。当然,由于显而易见的原因,您不能这样做。但那将是逆变(但同样,数组不是逆变的,它们仅支持协变)。

以下是来自 MSDN 的示例,我希望它们能向您展示逆变的真正含义(我现在拥有这些文档,所以如果您认为文档中有什么不清楚的地方,请随时给我反馈):

  1. 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());
  2. 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;
    }
  3. 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;

希望这对您有所帮助。

关于c# - 逆变解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1962629/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com