先看个例子,此代码在c# 4.0下可以编译通过,因为c#4.0才开始支持逆变和协变
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using
System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
public static void Main()
{
IEnumerable < object > objs = new List < string > (); //协变
Action < object > action1 = o => { };
Action < string > action2 = action1;//逆变
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
public static void Main()
{
IEnumerable < object > objs = new List < string > (); //协变
Action < object > action1 = o => { };
Action < string > action2 = action1;//逆变
}
}
}
下面是泛型接口IEnumerable<T>相关类型的定义
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public
interface
IEnumerable
<
out
T
>
: IEnumerable
{
// Summary:
// Returns an enumerator that iterates through the collection.
//
// Returns:
// A System.Collections.Generic.IEnumerator<T> that can be used to iterate through
// the collection.
IEnumerator < T > GetEnumerator();
}
public interface IEnumerator < out T > : IDisposable, IEnumerator
{
// Summary:
// Gets the element in the collection at the current position of the enumerator.
//
// Returns:
// The element in the collection at the current position of the enumerator.
T Current { get ; }
}
{
// Summary:
// Returns an enumerator that iterates through the collection.
//
// Returns:
// A System.Collections.Generic.IEnumerator<T> that can be used to iterate through
// the collection.
IEnumerator < T > GetEnumerator();
}
public interface IEnumerator < out T > : IDisposable, IEnumerator
{
// Summary:
// Gets the element in the collection at the current position of the enumerator.
//
// Returns:
// The element in the collection at the current position of the enumerator.
T Current { get ; }
}
可以看到T在IEnumerable定义中比以前多了out的修饰符。out表示T在泛型类型中只能被用作输出。也就是说T定义的对象是只会被用作赋值给客户代码中的引用。这样凡是使用IEnumerable<BaseType>类型的地方都可以被安全的替换成IEnumerable<SubType>.因为子类型(SubType)可以安全替换父类型(BaseType)
下面是泛型委托Action<T>的定义
public
delegate
void
Action
<
in
T
>
(T obj);
这里是用in修饰T。说明T在泛型中只会被用作输入,也就是说T定义的引用只能用作接受客户代码的赋值。所以使用Action<SubType>的地方都可以被安全的替换成Acton<BaseType>.道理和上面一样子类型(SubType)可以安全替换父类型(BaseType)。
总结:协变和逆变允许我们定义的泛型接口和泛型委托能更加的通用。而且保证不会破坏类型安全。