协变和逆变是NET Framework 4出来以后新增的特性
官方解释为:在 C# 和 Visual Basic 中,协变和逆变允许数组类型、委托类型和泛型类型参数进行隐式引用转换。 协变保留分配兼容性,逆变与之相反。
下面就用泛型为例来说明一下协变
//父类Base
class Base
{
}
//子类Derived
class Derived : Base
{
}
1.多态
static void Main(string[] args)
{
Derived d = new Derived();
Base b = d;
}
根据C#多态性,我们用父类引用b,指向子类对象d,这样是完全可以的
2.协变
static void Main(string[] args)
{
IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;
}
我们现在使用了泛型
创建一个List数组,数组被指定里面只允许存放Derived类型的对象,因为List实现了IEnumerable接口,所以可以用IEnumerable的引用指向它(为什么要这么做呢,因为我们想用d.GetEnumerator()去枚举List<Derived>里面的每个对象)
现在我们想把这个IEnumerable<Derived>赋给父类的IEnumerable引用(我相信还是有人会问,为什么要这样做,因为Base类不仅仅只有一个子类,只是为了说明协变性,而只建了一个子类,转为父类引用以后,那么可以用b.GetEnumerator()枚举所有子类的对象,只要b指向的是不同的子类,就不需要像d.GetEnumerator(),e.GetEnumerator(),f.GetEnumerator()这样为每个子类都要写一句,这其实就跟多态类似),因为我们习惯了多态的用法,所以认为这样是理所当然的事情,但在以前,C#不这么认为,C#认为IEnumerable<T>这个泛型参数T是恒定的,也就是我们指定了T为Derived以后,就不可以在改为其它类型了
但现在Framework 4中有了协变,并且IEnumerable<T>接口的参数T,被规定为了协变类型参数,这就把不可能变为可能了,也就是我们认为在多态性的情况下理所当然的事,协变性做了与多态性类似的事情,这样看起来就很自然
协变和逆变只是一个概念,在Framework 4以前也存在,只是没有明确的概念
上面的例子只是说明以前不可以的事,现在可以了,协变和逆变其实远远没有这么复杂
下面是msdn上的例子
static object GetObject() { return null; }
static void SetObject(object obj) { }
static string GetString() { return ""; }
static void SetString(string str) { }
static void Test()
{
// Covariance. A delegate specifies a return type as object,
// but you can assign a method that returns a string.
Func<object> del = GetString; //协变
//msdn上说,它们返回与委托类型指定的派生类型相比,派生程度更大的类型(协变)
//也就是说,原本是要返回string类型的,现在转换成了派生程度更大的object类型
// Contravariance. A delegate specifies a parameter type as string,
// but you can assign a method that takes an object.
Action<string> del2 = SetObject; //逆变
//msdn上说, 接受相比之下,派生程度更小的类型的参数(逆变)
//也就是说, 原本是需要接受object类型,现在接受了更小的string类型的参数
}
out其实跟协变和逆变没有直接的关系
在参数前加out,意思是,这个参数假如在方法里面被改变了值,那么方法外面的参数变量值也会改变
static void Main(string[] args)
{
int ii = 0;
program p = new program();
p.test(ii);
Console.WriteLine(ii);
}
public void test(int i)
{
i = 5;
}
在不使用out的情况下,我们将ii传入test方法,虽然方法中将值改变为了5,但是输出的时候仍然输出0
static void Main(string[] args)
{
int ii = 0;
program p = new program();
p.test(out ii);
Console.WriteLine(ii);
}
public void test(out int i)
{
i = 5;
}
在使用out的情况下,在方法里面改变了参数的值,变量ii也改变了,你确实可以理解为传入了引用或是地址或是指针一类的东西
常见的用法是在一个方法需要返回两个值的时候使用out,也就是将out作为返回值来用
[
本帖最后由 yhlvht 于 2013-4-8 10:11 编辑 ]