C# 协变和逆变

协变和逆变使我们在处理类层次结构时更加灵活。

在学习协变和逆变之前,请看以下类层次结构:

public class Small
{ 

}
public class Big: Small
{

}
public class Bigger : Big
{ 
    
}

根据上面的示例类,Small 是 Big 的基类,而Big 是 Bigger 的基类。这里要记住的一点是,派生类将始终具有比基类更多的东西,因此基类比派生类要小。

现在,看以下初始化:

类初始化

如上所示,基类可以容纳派生类,但派生类不能容纳基类。换句话说,一个实例即使要求很小也可以接受大,但是如果要求很大就不能接受小。

现在,让我们了解协变和逆变。

C# 协变(Covariance)

协变使您能够传递派生类型,其中需要基类型。协变就像同类型的方差。基类和其他派生类被认为是向基类型添加额外功能的同类类。因此,协变允许您在需要基类的地方使用派生类(如果需要小类,则 rule: 可以接受大类)。

协变可以应用于委托,泛型,数组,接口等。

委托协变

委托中的协变允许委托方法的返回类型具有灵活性。

public delegate Small covarDel(Big mc);

public class Program
{
    public static Big Method1(Big bg)
    {
        Console.WriteLine("Method1");
    
        return new Big();
    }
    public static Small Method2(Big bg)
    {
        Console.WriteLine("Method2");
    
        return new Small();
    }
        
    public static void Main(string[] args)
    {
        covarDel del = Method1;

        Small sm1 = del(new Big());

        del= Method2;
        Small sm2 = del(new Big());
    }
}
输出:
Method1
Method2

如您在上面的示例中看到的那样,委托期望返回的类型为 Small(基类),但是我们仍然可以分配返回Big(派生类)的Method1以及具有与委托期望的签名相同的Method2。

因此,协变允许您将方法分配给具有较少派生返回类型的委托。

C# 逆变 (Contravariane)

将Contravariane(逆变)应用于参数。 Cotravariance(逆变) 允许将基类参数分配给希望派生类参数的委托的方法。

继续上面的示例,添加具有与委托不同的参数类型的Method3:

delegate Small covarDel(Big mc);

class Program
{
    static Big Method1(Big bg)
    {
        Console.WriteLine("Method1");
        return new Big();
    }
    static Small Method2(Big bg)
    {
        Console.WriteLine("Method2");
        return new Small();
    }

    static Small Method3(Small sml)
    {
        Console.WriteLine("Method3");
        
        return new Small();
    }
    static void Main(string[] args)
    {
        covarDel del = Method1;
        del += Method2;
        del += Method3;

        Small sm = del(new Big());
}
输出:
Method1
Method2
Method3

如您看到的,Method3具有Small类的参数,而委托则期望Big类的参数。 不过,您可以将Method3与委托一起使用。

您还可以按如下所示的相同方法使用协变和逆变。

示例:协变和逆变的使用
delegate Small covarDel(Big mc);class Program
{    static Big Method4(Small sml)
    {        Console.WriteLine("Method3");    
        return new Big();
    }    static void Main(string[] args)
    {        covarDel del = Method4;    
        Small sm = del(new Big());
    }
}
输出:
Method4