接口
接口时一组抽象成员的命名集合。抽象方法是纯粹的协议,没有提供默认实现,接口表示某个类或结构可以选择去实现的行为。一个类或一个结构可以选择去支持任意数量的接口。
.NET基础类库中内置了几百个预定义的接口类型,由其他的类或结构实现。
接口与抽象积累的区别
同样抽象基类可以定义很多抽象成员来为所有的派生类型提供多态接口。
区别1:
但是抽象基类中可以有具体实现、字段和构造函数,但是在接口中只包含抽象成员。
如下代码:
public interface iTest
{
void Show()①
{
Console.WriteLine("sth");
}
void iTest();②
void iTest(int i);
private int i = 0;③
}
Yi_不能通过编译,因为接口中不能有实现;
Er_可以通过编译,因为接口中没有构造函数的概念,只当做普通的抽象成员;
San_不能通过编译,因为接口中不能有字段。
而在抽象积累中可以存在上述成员。
区别2:
接口可以被任意的类或结构实现,而抽象积累的抽象成员只能被派生类型访问。
.NET基础类库中很多看似无关的类型都实现了相同的接口,比如:Icloneable。不能的类支持相同的接口,做多态处理。
区别3:
类或结构可以支持任意数量的接口,但是不能实现多重继承。
造成问题:比如说BMW既是Car,又支持克隆,可是不允许多重继承,怎么办?
这就是为什么要引入接口。用接口去实现克隆这一行为。
区别4:
继承抽象基类的抽象成员,必须全部实现,否则无法通过编译。抽象成员是不能包含在非抽象类中,否则无法通过编译,如下:
public class test5 : test4
{
public abstract void Func();
public abstract void Func1();
}
用抽象基类去继承抽象基类也是不可行的,test5会覆盖掉test4中的抽象方法,无法通过编译,如下:
public abstract class test5 : test4
{
public abstract void Func();
public abstract void Func1();
}
如果需要重写抽象基类中的抽象方法,需要关键字override。
插入0:如果方法没有声明为抽象成员,则必须给出实现,否则无法通过编译,如下:
private void show();
如下是使用抽象基类的正确方法:
public class test5 : test4
{
public override void Func()
{
Console.WriteLine("11");
}
public override void Func1()
{
Console.WriteLine("12");
}
}
接口其他性质:
接口作为方法参数,可以把任意支持接口的对象(类的实例)作为参数传入。
插入:抽象成员,比如说抽象方法,必须显示声明为共有的,否则无法通过编译。只要是用abstract声明的抽象方法,就不能有实现。
插入2:基类的可访问性不能比继承类的可访问性低,比如说积累默认为private,继承类显示声明为public,是不能通过编译的。
插入3:命名空间中定义的元素,不能显示的定义为private\protected\protected internal,比如说private class是不能通过编译的。
如何自定义接口
接口也是cs文件,接口时可以实现接口的,如下:
interface Interface1:IEnumerable
{
IEnumerator GetEnumerator();
}
不可以在接口的成员前用修饰符进行修饰,因为所有的成员都是隐式公共和抽象的。
接口不能够有字段/构造函数/实现。接口不能实例化。
接口支持属性。
接口只有被类或结构实现,才有具体意思,因为接口时描述行为的。
插入5:结构总是从System.ValueType继承。
相同点1:
类实现接口的时候,总是实现所有的抽象成员。
且必须声明为public,否则无法通过编译。抽象成员不能是私有的。因为抽象成员不能够由对象实例来调用,如果声明为私有的将无法访问,这一点在接口和类中均是一致的,只不过在接口中默认为public abstract,不能显示定义为public。
相同点2:
实现基类中的抽象成员或者实现接口的抽象成员时,必须声明为public。
相同点3:
抽象基类和接口的抽象属性,均可以被继承类或支持的类实现。
在对象级别调用接口成员
如hex.Points;//属性
可能在某些情况下,在编译阶段,无法判断指定类型是否支持接口。我们可以通过三种方式获取接口引用:
1、强制类型转换+Try…Catch,如下:
try
{
Interface1 temp = (Interface1)item;
Console.WriteLine(temp.Points);
}
catch (Exception)
{
throw;
}
2、as关键字
Interface1 temp = item as Interface1;
if (temp != null)
{
Console.WriteLine(temp.Points);
}
3、is关键字
if (item is Interface1)
{
Console.WriteLine(((Interface1)item).Points);}
注意强制类型转换不是就进原则,而是针对整体的,故而上面的表达式又添加了一层括号。
接口作为参数
接口作为返回值
接口数组
数组可以包含实现了该接口的所有类或结构
使用VS实现接口
利用智能标签,选择实现接口或显示实现接口:
显示接口实现
当支持的多个接口具有相同的抽象成员时,如Interfacea\Interfaceb\Interfacec中均包含方法draw时,如下显示实现接口成员:
void InterfaceName.FuncName();
但是显示接口实现的访问修饰符不能为public,否则编译器报错。所以不得不使用强制类型转换来调用指定的方法。
设计接口的层次结构
接口拓展了既有接口,不必重新声明原接口中的抽象成员,当然声明后编译正常,但是会把原接口中的抽象成员覆盖掉,如下:不必再次声明GetEnumerator
interface Interface1:IEnumerable
{
//IEnumerator GetEnumerator();
//int MyProperty { get; set; }
byte Points { get; }
}
如类A实现了Interface1也需要实现GetEnumrator抽象方法。
显示接口实现为private
隐式接口实现为public
第二部分 内置接口类型的使用
构建可枚举类型:支持Ienumerable
如:
class test0:IEnumerable
{
int[] testint = new int[4] { 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
return testint.GetEnumerator();
}
}
static void Main(string[] args)
{
test0 t = new test0();
foreach (int i in t)
{
Console.WriteLine(i);
}
Console.ReadKey();
}
因为test0类实现了接口Ienumerable,所以支持遍历。
因为在test0中GetEnumrator是public(公开的)
test0 t = new test0();
IEnumerator r = t.GetEnumerator();
r.MoveNext();
r.MoveNext();
Console.WriteLine(r.Current);
默认Current为0现在为2;
Yield构建迭代器
class test0:IEnumerable
{
int[] testint = new int[4] { 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
//return testint.GetEnumerator();
foreach (int i in testint)
{
yield return i;
}
}
}
yield构建命名迭代器
相当于定义一个方法,按照指定的方式进行迭代:在使用时直接由对象调用方法即可,命名迭代器就是方法,如:
public IEnumerable Get()
{
yield return testint[3];
yield return testint[0];
yield return testint[2];
yield return testint[1];
}
test0 t = new test0();
foreach (int i in t.Get())
{
Console.WriteLine(i);
};
构建可克隆的对象:Icloneable
插入6:构造函数必须为公开的;
class qq :Icloneable
{
public int x;
public qq(int i)
{
x = i;
}
public object Clone()
{
return new qq(this.x);
}
public override string ToString()
{
return string.Format("nihao{0}",x);
}
}
如果仅仅是这样的话,直接接口Icloneable没有作用,因为我们就是在调用Clone方法。
return this.MemberwiseClone();
可以调用这个方法直接返回浅显的副本。
接口的意义是实现多态:即多种类型实现同一种行为的不同处理方式,所以当写完方法以后,去掉声明的接口也是完全没有影响的,因为我们就是去调用方法了。
构建可比较的对象(Icomparable)
public int CompareTo(object obj)
{
Car temp = obj as Car;
if (temp != null)
{
//if (temp.ID < this.ID)
// return 1;
//else if (temp.ID == this.ID)
// return 0;
//else
// return -1;
return this.ID.CompareTo(temp.ID);
}
else
{
//return -2;
throw new Exception();
}
总结:
接口:抽象成员的集合,不提供任何实现细节。
与抽象基类的区别:
1,抽象基类中可以有具体实现、字段和构造函数。但是接口中不可以。接口中没有构造函数的概念,故而可以声明和类同名的构造方法。
2,接口可以被任意的类或结构实现,而抽象积累的抽象成员只能被派生类型访问。
3,类或结构可以支持多个接口,但不可以多重继承。
相同点:
1,支持接口必须支持接口的所有抽象成员(包括属性),继承抽象基类需要实现所有抽象成员。
抽象基类的特点:
抽象成员在实体类中必须override;
在抽象类中,如果方法没有标明abstract,必须实现。
接口其他特性:
接口作为方法参数,对象作为参数传入;
实现接口的抽象成员,默认情况下,必须声明为public
接口中的抽象成员不允许访问修饰符。
接口支持属性。
在对象级别上调用接口成员:as/is/强制类型转换
接口显示实现:支持多个接口时使用。不能声明为public。
内置接口类型的使用
创建可枚举类型
创建可克隆的对象
构建可比较的对象