泛型接口
- 当接口中定义的方法需要带上泛型参数时,可以定义泛型接口
public interface IComparable<in T>
{
int CompareTo(T other);
}
public class Person : IComparable<Person>
{
public int CompareTo(Person other) => LastName.CompareTo(other.LastName);
}
作为对比:如果不定义泛型接口,而是用IComparable
接口,则需多做一个object
到T
的类型转换
public class Person : IComparable
{
public int CompareTo(object obj)
{
Person other = obj as Person; //将obj转换成Person
return this.lastname.CompareTo(other.LastName);
}
}
协变和抗变
协变和抗变,是指对参数和返回值的类型进行转换 有如下前提条件: Rectangle继承了Shape
public class Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override string ToString () => $"Width: {Width}, Height: {Height}"
}
public class Rectangle : Shape
{
}
.NET中,方法的参数是抗变的,如下示例:
public void Display(Shape o) {}
var r = new Rectangle { Width = 5, Height = 2.5 };
Display(r); //编译器接受Rectangle类型传给Shape类型
.NET中,方法的返回值是协变的,如下示例:
public Rectangle GetRectangle();
Shape s = GetRectangle();
泛型接口,如果不加in和out,那就是不变的,而不是协变或抗变的
泛型接口的抗变
- 加了
in
, 就意味着泛型接口IDisplay<in T>
是抗变的了 - 加了
in
,接口IDisplay<in Shape>
就:- 只能把
Shape
当方法的输入来用 - 把
Shape
作为方法入参的事儿,Rectangle
也一样能代劳
- 只能把
- 因此,允许用
IDisplay<Rectangle>
调用只在IDisplay<Shape>
定义的Show方法,这叫泛型接口IDisplay<in T>
的抗变
泛型接口
I接口<in T>
的抗变:子类
类型允许传给基类
作为类型的方法参数,因此,允许用I接口<子类>
调用只在I接口<基类>
定义的方法
所谓“抗”,“抗”的是:IX<基>变IX<子>之后,“能力”的变化。也就是说,IX<基>变IX<子>,IX<基>的“能力”,依然被IX<子>所持有。
public interface IDisplay<in T>
{
void Show(T item);
}
public class ShapeDisplay : IDisplay<Shape>
{
public void Show(Shape s) => Console.WriteLine($"{s.GetType().Name} Width: {s.Width}, Height: {s.Height}");
}
static void Main()
{
IDisplay<Shape> shapeDisplay = new ShapeDisplay();
IDisplay<Rectangle> rectangleDisplay = shapeDisplay;
rectangleDisplay.Show(rectangles[0]);
}
泛型接口的协变
- 加了
out
, 就意味着泛型接口IIndex<out T>
是协变的了 - 加了
out
,接口IIndex<out Rectangle>
就:- 只能把
Rectangle
作为方法的返回类型 IIndex<out Rectangle>
的方法的返回类型永远都是Rectangle
- 因此,即便下一步是将
IIndex<out Rectangle>
赋给IIndex<out Shape>
, 这个Shape
其实拥有Rectangle
的能力。 - 用
IIndex<out Shape>
也可以放心调用只在IIndex<out Rectangle>
中定义的方法,这叫泛型接口IIndex<out T>
的协变
- 只能把
泛型接口
I接口<in T>
的协变:允许用I接口<基类>
调用只在I接口<子类>
定义的方法
所谓“协”,“协”的是:IX<子>变IX<基>之后,“能力”的变化。也就是说,IX<子>变IX<基>,IX<子>的“能力”,居然能被IX<基>所持有。
public interface IIndex<out T>
{
T this[int index] { get; }
int Count { get; }
}
public class RectangleCollection : IIndex<Rectangle>
{
private Rectangle[] data = new Rectangle[3]
{
new Rectangle { Height=2, Width=5 },
new Rectangle { Height=3, Width=7},
new Rectangle { Height=4.5, Width=2.9}
};
private static RectangleCollection coll;
public static RectangleCollection GetRectangles() => coll ?? (coll = new RectangleCollection());
public Rectangle this[int index]
{
get
{
if (index < 0 || index > data.Length)
throw new ArgumentOutOfRangeException(nameof(index));
return data[index];
}
}
public int Count => data.Length;
}
static void Main()
{
IIndex<Rectangle> rectangles = RectangleCollection.GetRectangles();
IIndex<Shape> shapes = rectangles;
for (int i = 0; i < shapes.Count; i++)
{
Console.WriteLine(shapes[i]);
}
}