首页 > Python基础教程 >
-
C#教程之接口的实现方式(显示和隐示)及协变和
如果一个类继承了两个不同的接口,且这两个接口有一样的成员,类实例任意调用I1,I2接口:
如:
public interface I1
{
string GetSome();
}
public interface I2
{
string GetSome();
}
public class MyClass : I1, I2
{
public string GetSome()
{
return "Some";
}
}
MyClass c1 = new MyClass();
I1 i1 = (I1) c1;
I2 i2 = (I2) c1;
c1.GetSome();
i1.GetSome();
i2.GetSome();
但是通常不同接口即使成员名称相同,返回值相同,实现的目的功能还是不一样的。所以区分接口还是非常必须要的。那么如何在类里区分继承了两个有相同约束的接口呢?
接口显示实现
显示实现I1接口(接口名称.成员)
public class MyClass : I1, I2
{
public string GetSome()
{
return "Some";
}
//接口成员的访问修饰符默认为public,且不能显示实现和修改,同样显示实现接口的类的成员也不可以有访问修饰符。
string I1.GetSome()
{
return "I1.Some";
}
}
这时i1.GetSome()的输出就是"I1.Some",i2.GetSome()和c1.GetSome()不变为"Some"
只显示继承I1接口,不实现I2接口
public class MyClass : I1, I2
{
string I1.GetSome()
{
return "I1.Some";
}
}
编译报错,因为没有实现接口I2。所以即使I1,I2有相同的约束,显示实现是区分I1和I2的。隐示实现的话不区分I1,I2,可以只写一个方法。
只显示实现I1接口
public class MyClass : I1
{
string I1.GetSome()
{
return "I1.Some";
}
}
只显示实现接口I1,必须用接口来调用,不可以使用类实例来访问GetSome()方法
//错误
MyClass my1=new MyClass();
my.GetSome();
//正确
I1 i1=(I1) my1;
i1.GetSome();
//正确
I1 i1=new MyClass();
i1.GetSome();
所以当类实现多个有冲突的成员的接口时,显示使用接口可以解决这些冲突的接口成员。
泛型接口的协变和逆变
两个有继承关系的类
public class Parent { }
public class Child : Parent { }
类相互间转换
Parent parent=new Child();//正常转换
Child child=new Parent();//禁止转换
Child child=(Child)new Parent();//禁止强制转换
Parent[] arrParent = new Child[] {};//正常转换
子类可以安全的转换为父类,反之则不行。同理数组间的转换也遵循这个原则。
所以一个子类到父类的可变性称之为协变,反之为逆变。具体在泛型接口上体现为参数的in,out关键字的使用。
例如:IEnumerable泛型的实现是IEnumerable<out T>,List泛型实现是List<T>。前者可以实现协变,后者不可以。所以只有添加了out关键字的泛型接口才可以实现协变(子类转换为父类)。
泛型接口参数没有关键字
public interface TI<T>{}
public class ParentCollection : TI<Parent>{}
public class ChildCollection : TI<Child>{}
TI<Parent> Parents = new ChildCollection();//编译错误
TI<Child> Childs = new ParentCollection();//编译错误
如果泛型接口的参数没有使用in,out关键字,那么不管是协变还是逆变都是不能实现的。
泛型接口参数带有out关键字
public interface TI<out T>
{
T Get();
}
public class ParentCollection : TI<Parent>
{
public Parent Get()
{
return new Parent();
}
}
public class ChildCollection : TI<Child>
{
public Child Get()
{
return new Child();
}
}
调用实现out参数接口的类
TI<Parent> Parents = new ChildCollection();//协变转换
TI<Child> Childs = new ParentCollection();//编译错误,无法转换
总结:实现out关键字参数的接口可以实现协变(子类转换为父类)。
泛型参数接口带有in关键字
public interface TI<in T>
{
void Method(T param);
}
public class ParentCollection : TI<Parent>
{
public void Method(Parent p){}
}
public class ChildCollection : TI<Child>
{
public void Method(Child c){}
}
调用实现in参数接口的类
TI<Parent> Parents = new ChildCollection();//编译错误
TI<Child> Childs = new ParentCollection();//逆变转换
总结:带有in参数接口的类可以实现逆变转换(父类转换为子类)
对比发现实现in,out参数接口的方法是不一样的。
//协变
public interface TI<out T>
{
T Get();
void Method(T param);//编译错误,只能定义在逆变的接口
}
//逆变
public interface TI<in T>
{
T Get();//编译错误,只能定义在协变的接口
void Method(T param);
}
总结:协变的泛型类型只能作为输出类型,不能作为输入类型(out关键字只能影响输出类型),逆变的泛型类型只能作为输入类型,不能作为输出类型(in关键字只能影响输入类型)