C#的协变和逆变是在面向对象里面的类型系统中的概念。在C# 2.0之前,这两个概念是不存在的,开发者只能通过强制类型转换来满足某些需求。在C# 2.0之后,引入了这两个概念,通过它们可以更加安全地进行类型转换,同时也提升了代码的可读性。
一、协变:
协变指的是能够将一个派生类的变量赋值给基类的变量,或者能够将一个方法的返回值类型声明为基类的类型。它的形态如下:
interface IAnimal { }
class Dog : IAnimal { }
class Cat : IAnimal { }
// 可以将Dog类型的变量赋值给IAnimal类型的变量
IAnimal animal = new Dog();
// 可以将返回值类型声明为IAnimal类型
IAnimal GetAnimal() { return new Dog(); }
二、逆变:
逆变指的是能够将一个基类的变量赋值给派生类的变量,或者可以将一个方法的参数类型声明为派生类的类型。它的形态如下:
interface IAnimal { }
class Dog : IAnimal { }
class Cat : IAnimal { }
// 可以将IAnimal类型的变量赋值给Dog类型的变量
Dog dog = (Dog)new IAnimal();
// 可以将参数类型声明为Dog类型
void AddDog(Dog dog) { }
示例一:协变
interface ILogger<out T> {
T Log();
}
class DbLogger<T> : ILogger<T> {
public T Log() {
Console.WriteLine("DbLogger Log: " + typeof(T).ToString());
return default(T);
}
}
class FileLogger<T> : ILogger<T> {
public T Log() {
Console.WriteLine("FileLogger Log: " + typeof(T).ToString());
return default(T);
}
}
ILogger<object> logger1 = new DbLogger<object>();
ILogger<object> logger2 = new FileLogger<object>();
上面的例子中,ILogger接口有一个占位符类型T,用于表示的具体的类型是什么。DbLogger和FileLogger都有一个Log方法,返回值类型为T。我们通过ILogger
示例二:逆变
delegate T Func<T>();
delegate void Set<in T>(T value);
class Fruit { }
class Apple : Fruit { }
class Msg { }
class WarningMsg : Msg { }
void SetFruit(Set<Fruit> setFruit) { }
void GetApple(Func<Apple> getApple) { }
Set<Apple> setApple = a => { Console.WriteLine("Set Apple"); };
SetFruit(setApple); // 逆变
Func<Fruit> getFruit = () => { Console.WriteLine("Get Fruit"); return new Apple(); };
GetApple(getFruit); // 逆变
上面的例子中,我们先声明了两个delegate,Func和Set。它们分别有一个类型参数T,并且Set的类型参数类型是逆变的,Func的类型参数类型是协变的。然后我们声明了两个类Fruit和Apple,并且Apple是Fruit的子类。同样地,我们还声明了两个类Msg和WarningMsg,并且WarningMsg是Msg的子类。
接着,我们定义了两个方法SetFruit和GetApple。SetFruit要求传入一个Set
同理,我们声明一个Func
总结:
协变和逆变可以让我们使用更加灵活的泛型类型,提高程序的安全性和可读性。协变是将派生类赋值给基类或者将返回值类型声明为基类,逆变是将基类赋值给派生类或者将参数类型声明为派生类。在编程中,我们可以根据需求来采用协变和逆变的形式,灵活运用泛型类型,来更加高效地编写代码。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:你了解C#的协变和逆变吗,看完这篇就懂了 - Python技术站