不变量
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
interface IWrapper<T>
{
void SetData(T data);
T GetData();
}
class Wrapper<T> : IWrapper<T>
{
private T storedData;
void IWrapper<T>.SetData(T data)
{
this.storedData = data;
}
//显式实现接口,调用时只能通过接口引用来进行调用
T IWrapper<T>.GetData()
{
return this.storedData;
}
}
class Program
{
static void Main(string[] args)
{
Wrapper<string> stringWrapper = new Wrapper<string>();
//这句话编译没问题,但是在执行的时候会抛出异常
//无法执行强转
IWrapper<object> storedStringWrapper = (IWrapper<object>)stringWrapper;
storedStringWrapper.SetData("hello");
Console.WriteLine(storedStringWrapper.GetData());
}
}
}
上面这段代码中的IWrapper接口是不变量,也就是说,我们不能将IWrapper对象赋给IWrapper类型的引用,就算A由B派生
协变接口
先看下面的代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Test
{
private int day { set; get; }
public void Display()
{
Console.WriteLine("SSS");
}
}
interface IStoreWrapper<T>
{
void SetData(T data);
}
interface IWrapper<out T>
{
T GetData();
}
//struct T
//{
// public int X { get; set; }
// public int Y { get; set; }
//}
class Wrapper<T> : IWrapper<T>, IStoreWrapper<T>
{
private T storedData;
void IStoreWrapper<T>.SetData(T data)
{
this.storedData = data;
}
//显式实现接口,调用时只能通过接口引用来进行调用
T IWrapper<T>.GetData()
{
return this.storedData;
}
}
class Program
{
static void Main(string[] args)
{
Wrapper<string> stringWrapper = new Wrapper<string>();
IStoreWrapper<string> setStringWrapper = stringWrapper;
//IWrapper接口只有一个GetData方法,不会对数据进行任何修改
//对于泛型接口定义的方法,如果类型参数T仅在方法的返回值中出现
//可通过out关键字来告诉编译器,一些隐式转换是合法的
//可以转换成其他类型,但是这个转换是有条件的,这里我随便创建了一个Test类
//这样是无法编译成功的,就那这里的借口说吧,现在我要把Wrapper<A>对象赋给
//IWrapper<B>引用,那么B一定要是A的基类,就是说A一定要派生自B
IWrapper<Test> getStringWrapper = stringWrapper;
setStringWrapper.SetData("hello");
Console.WriteLine(getStringWrapper.GetData());
}
}
}
在上面的代码中,我们就可以进行隐式类型转换了
因为IWrapper接口只有一个GetData方法,不会对数据进行任何修改,对于泛型接口定义的方法,如果类型参数T仅在方法的返回值中出现,可通过out关键字来告诉编译器,一些隐式转换是合法的
C#中的这个功能,就叫做协变性,拥有这种功能的接口,就是协变接口
逆变接口
有协变接口,那么自然就有逆变接口,它允许使用泛型接口通过A类型(比如String)的一个引用来引用B类型(比如Object)的一个对象,只要A从B派生
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class ObjectComparer : IComparer<Object>
{
int IComparer<Object>.Compare(Object x, Object y)
{
int xHash = x.GetHashCode();
int yHash = y.GetHashCode();
if (xHash == yHash)
return 0;
if (xHash < yHash)
return -1;
return 1;
}
}
class Program
{
static void Main(string[] args)
{
Object x = 1;
Object y = 2;
ObjectComparer objectComparer = new ObjectComparer();
IComparer<Object> iComparer1 = objectComparer;
Console.WriteLine(iComparer1.Compare(x, y));
IComparer<String> iComparer2 = objectComparer;
Console.WriteLine(iComparer2.Compare("asd", "dsa"));
}
}
}
上面代码中的IComparer接口就是具有逆变性的接口,IComparer<string>可以和IComparer<object>
引用同一个对象,因为在IComparer接口的实现中:
namespace System.Collections.Generic
{
public interface IComparer<in T>
{
int Compare(T x, T y);
}
}
IComparer的类型参数中有in
关键字,这样一来,IComparer<string>就可以引用IComparer<object>
类型的对象,因为string是object的一个派生类,只要前者的派生程度高于后者就可进行这种操作