C#中不支持多重继承。为了避免传统的多重继承给程序带来复杂性的问题,同时保证多重继承给程序员带来的好处,提出了接口的概念。
1、接口
(1)接口的概念以及声明
接口提出了一种契约,让使用接口的程序设计人员必须严格遵守接口提出的约定。强制性的要求实现子类,(即完全实现某个接口的派生类)必须实现接口约定的规范,从而保证子类必须拥有某些特性。
接口可以包含方法、属性、索引器和事件作为成员,但是并不能设置这些成员的具体值,也就是说,只能定义,不能给他里面定义的东西赋值。接口可以继承其他接口,类可以通过其继承的基类或接口多次继承同一个接口。
接口的特征:
接口类似于抽象基类:继承接口的任何非抽象类型必须实现接口中的所有成员。
不能够直接实例化接口。
接口可以包含事件,索引器,方法和属性。
类和结构可以从多个接口继承。
接口自身可以从多个接口继承。
语法:
声明接口的时候,使用Interface关键字。
修饰符 Interface 接口名称 :继承的接口列表
{
接口内容;
}
(2)接口的实现以及多重继承
接口的实现通过类继承来实现,一个类虽然只能够继承一个基类,但是可以继承任意接口。声明实现接口的类时,需要在基类列表中包含类所实现接口的名称。
示例程序:说明接口的创建和使用
namespace InterFace
{
interface IStudent
{
string StudentCode
{
get;
set;
}
string StudentName
{
get;
set;
}
void ShowInfoOfStudent();
}
class Program : IStudent
{
string strId = "";
string strName = "";
public string StudentCode
{
get
{
return strId;
}
set
{
strId = value;
}
}
public string StudentName
{
get
{
return strName;
}
set
{
strName = value;
}
}
public void ShowInfoOfStudent()
{
Console.WriteLine("编号\t姓名");
Console.WriteLine(StudentCode+"\t"+StudentName);
}
static void Main(string[] args)//应用程序主程序
{
Program pro = new Program();//实例化program类对象
IStudent iSu = pro;//用派生类对象实例化接口
iSu.StudentCode = "TM";//为派生类的属性赋值
iSu.StudentName = "C#";
iSu.ShowInfoOfStudent();//调用派生类中的方法显示属性
}
}
}
注意:接口可以多重继承,继承的时候用‘,’分隔。
示例程序:多继承实例:
namespace Interface2
{
interface IPeaple
{
string Name
{
get;
set;
}
string Sex
{
get;
set;
}
}
interface ITeacher : IPeaple
{
void teach();
}
interface IStudent : IPeaple
{
void study();
}
class Program : ITeacher , IStudent
{
string name = "";
string sex = "";
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public string Sex
{
get
{
return sex;
}
set
{
sex = value;
}
}
public void teach()
{
Console.WriteLine(Name+" "+Sex+" 教师");
}
public void study()
{
Console.WriteLine(Name+" "+Sex+" 学生");
}
static void Main(string[] args)
{
Program pro = new Program();
ITeacher iteacher = pro;
iteacher.Name = "DK";
iteacher.Sex = "man";
iteacher.teach();
IStudent istudent = pro;
istudent.Name = "lich";
istudent.Sex = "man";
istudent.study();
}
}
}
注意:在派生类当中,继承下来的Sex读写接口和派生类中的成员sex必须保持不一致,否则会导致错误。一般使用成员开头大写来表示读写。具体参考:成神之路
(3)显式接口成员显示
如果类实现两个接口,并且这两个接口包含具有相同签名的成员,那么在类中实现该成员,将会导致两个接口都使用该成员作为他们的实现。然而,如果两个接口成员实现不同的功能。那么这可能会导致其中一个接口的实现不正确或者两个接口的实现都不正确。这时可以显式的实现接口成员,即创建一个仅仅透过该接口调用并且特定于该接口的类成员。显式接口成员实现是使用接口名称和一个句点命名该类成员来实现的。
示例程序:声明两个接口,两个接口生命了一个同名方法,然后定义一个类,继承这两个接口。
namespace Interface3
{
interface ICalculate1
{
int Add();
}
interface ICalculate2
{
int Add();
}
class Compute : ICalculate1, ICalculate2
{
int ICalculate1.Add()
{
int x = 40;
int y = 10;
return x + y;
}
int ICalculate2.Add()
{
int x = 10;
int y = 40;
int z = 50;
return x + y + z;
}
}
class Program
{
static void Main(string[] args)
{
Compute conputer = new Compute();
ICalculate1 Cal1 = conputer;
Console.WriteLine(Cal1.Add());
ICalculate2 Cal2 = conputer;
Console.WriteLine(Cal2.Add());
}
}
}
注意:显式接口成员实现中不能包含访问修饰符,abstract,virtual,override或static修饰符。
2、抽象类和抽象方法
如果一个类不与具体的事物联系,而只是表达一种抽象的概念或行为,仅仅是作为其派生类的一个基类,这样的类就可以声明为抽象类,在抽象类中声明方法时,如果加上abstract关键字,则为抽象方法。
C#中规定只要有一个方法声明为抽象方法,这个类也必须声明为抽象类。
(和C++一样)
(1)抽象类概述
抽象类主要用来提供多个派生类可以共享的基类的公共定义,它与非抽象类的主要区别:
抽象类不能直接实例化。
抽象类不能被密封。
抽象类中可以包含抽象成员,但是非抽象类中不可以。
语法:C#中声明抽象类的时候需要使用abstract关键字,具体格式:
访问修饰符 abstract class 类名 : 基类或者接口
{
//类成员
}
注意:声明抽象类的时候,除了abstract关键字,class关键字和类名外,其他都是可选的。
(2)抽象方法概述和声明
抽象方法就是在声明方法的时候加上abstract关键字,声明抽象方法的时候需要注意:
抽象方法必须声明在抽象类中。
声明抽象方法时,不能使用virtual、static 和private修饰符。
抽象方法声明引入了一个新方法,但是不提供该方法的实现,由于抽象方法不提供任何世纪实现,因此抽象方法的方法体只包含一个分号。
当从抽象类派生一个非抽象类的时候,需要在非抽象类中重写抽象方法,以提供具体的实现。重写抽象方法时需要使用override关键字。
示例程序:关于抽象类和抽象方法的使用:
namespace @abstract
{
public abstract class Employee
{
private string strCode = "";
private string strName = "";
public string Code
{
get
{
return strCode;
}
set
{
strCode = value;
}
}
public string Name
{
get {
return strName;
}
set
{
strName = value;
}
}
public abstract void ShowInfoOfEmployee();
//抽象方法,用来输出信息。
}
public class MREmployee : Employee
{
public override void ShowInfoOfEmployee()
{
Console.WriteLine("information:\n"+Code+" "+Name);
}
}
class Program
{
static void Main(string[] args)
{
MREmployee mr = new MREmployee();
Employee emp = mr;
emp.Code = "killer";
emp.Name = "is dead";
emp.ShowInfoOfEmployee();
}
}
}
(3)抽象类和接口
抽象类和接口都包含可以由派生类继承的成员,他们都不能直接实例化。但是可以声明他们的变量。如果这样做,就可以使用多态性把继承这两种类型的对象指定给他们的变量。接着通过这些变量来使用这些类型的成员,但是不能直接访问派生类中的其他成员。
抽象类和接口的区别:
他们的派生类只能继承一个基类,即只能直接继承一个抽象类,但是可以继承任意多个接口。
抽象类中可以定义成员的实现。但是接口中不可以。
抽象类中可以包含字段,构造函数,析构函数,静态成员或者常量等,接口中不可以。
抽象类中的成员可以是私有的(只要不是抽象的)、受保护的、内部的或受保护的内部成员(受保护的内部成员只能在应用程序的代码或派生类中访问),但是接口中的成员必须是公共的。
3、密封类和密封方法
如果所有的类都可以被继承,那么很容易导致继承的滥用,进而使类的层次结构体系变得十分复杂。避免滥用继承,C#中提出了密封类的概念。
(1)密封类概述
密封类可以用来限制扩展性,如果密封了某个类,则其他类不能从该类继承;如果密封了某个成员,则派生类不能够重写该成员的实现。默认情况下,不应该密封类型和成员。密封可以防止对库的类型和成员进行自定义,但是也会影响开发人员对可用性的认识。
密封类的语法:
访问修饰符 sealed class 类名:基类或者接口
{
//密封类的成员
}
(2)密封方法概述
并不是每个方法都可以声明为密封方法,密封方法只能够用于对基类的虚方法进行实现,并提供具体的实现,所以,声明密封方法时。sealed修饰符总是和override修饰符同时使用。
示例程序:
public class BaseTest
{
public virtual void mymethod()
{
console.writeline("XXXX");
}
public sealed class DeriveTest : BaseTest//派生一个密封的子类
{
public sealed override void mymethod()//密封兵并重写基类中的方法
{
base.mymethod();//注意,表示调用基类中的相关方法。base关键字提供了一个简写方法。
console.writeline("XXXXX");
}
}
}
(3)密封类和密封方法的使用
密封类除了不能被继承之外,与非密封类的用法大致相同。而密封方法则必须通过重写基类中的虚方法来实现。
示例程序:密封类和方法的实际运用:
namespace SealedTest
{
public class Peaple
{
public virtual void ShowInfoOfPeaple()
{ }
}
public sealed class Student : Peaple
{
private string strCode = " ";
private string strName = " ";
public string Code
{
get
{
return strCode;
}
set
{
strCode = value;
}
}
public string Name
{
get
{
return strName;
}
set
{
strCode = value;
}
}
public sealed override void ShowInfoOfPeaple()
{
Console.WriteLine("the information of the student:\n"+Code+" "+Name);
}
}
class Program
{
static void Main(string[] args)
{
Student stu = new Student();
stu.Code = "killer";
stu.Name = "is dead";
stu.ShowInfoOfPeaple();
}
}
}