OOP三大特性:封装,继承,多态。封装就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或对象操作,对不可信的进行信息隐藏。在C#中,OOP技术的一个应用就是WPF桌面应用程序。
对象是OOP应用程序的组成部件,类是用于实例化对象的类型定义。对象可以包含数据,提供其他代码可以使用的操作。数据可以通过属性供外部代码使用,操作可以通过方法供外部代码使用。属性和方法都称为类的成员。属性可以进行读取访问,写入访问或读写访问。在.NET 中,所有的东西都是对象。
C#使用 class
关键字来定义类:
class MyClass : MyBase, MyInterface, MySecInterface //用逗号将基类名和接口名分隔开,如果未指定基类,接口就跟在冒号的后面。
{
//code
}
默认情况下,类声明为内部的,即只有当前项目中的代码才能访问它。可使用 internal 访问修饰符关键字来显式地指定这一点。另外,还可以使用 public 关键字指定类是公共的,abstract 关键字指定类是抽象的(可以是公共抽象类,也可以是内部抽象类),sealed 关键字指定类是密封的(可以是公共密封类,也可以是内部密封类)。还可以在类定义中指定继承,需要在类名的后面加一个冒号,其后是基类名。编译器不允许派生类的可访问性高于基类。如果没有使用基类,被定义的类就只继承于基类System.Object
。类里面还可以嵌套类。
类定义中的所有成员(字段,方法和属性)都有自己的访问级别:
字段也可以使用关键字readonly
,表示这个字段只能在执行构造函数的过程中赋值,或由初始化赋值语句赋值。定义方法时也可以使用下述关键字:virtual(方法可以重写),abstract(方法必须在非抽象的派生类中重写,只用于抽象类中),override(方法重写了一个基类方法),extern(方法定义放在其他地方)。属性也可以使用virtual, abstract, override关键字,但这些关键字不能用于字段。属性定义方式与字段类似,但包含的内容比较多。属性拥有两个类似于函数的块,一个块用于获取属性的值,另一个块用于设置属性的值。这两个块也称为访问器,分别用get和set关键字来定义,用于控制属性的访问级别。可以忽略其中的一个块来创建只读或只写属性。当然,这仅适用于外部代码,因为类中的其他代码可以访问这些代码块能访问的数据。还可以在访问器上包含可访问修饰符,例如使get块变成公共的,使set块变成受保护的。C#6引入了一个名为“基于表达式的属性”的功能。例如:
private int myDoubledInt = 5;
public int MyDoubledIntProp => (myDoubledInt * 2);
在添加属性时有一项很方便的技术,可以从字段中生成属性:在代码视图中右击某个成员,选择“快速操作和重构”,这样就可以减少为该字段创建属性的时间。
当从基类继承一个(非抽象的)成员时,也就继承了其实现代码。如果继承的成员是虚拟的,就可以用 override 关键字重写这段实现代码。如果无论继承的成员是否为虚拟,我们想隐藏这些实现代码,则可以使用new关键字显式地表明意图,同时还可以通过基类访问它,但重写不能通过基类访问它。
无论是重写成员还是隐藏成员,都可以在派生类的内部访问基类成员,可使用 base 关键字,它表示包含在派生类中的基类的实现代码。例如:
public class MyBaseClass
{
public virtual void DoSomething()
{
//Base implementation
}
}
public class MyDerivedClass : MyBaseClass
{
public override void DoSomething()
{
base.DoSomething();
//Other implementation
}
}
除了 base 关键字,还可以使用 this 关键字,只是 this 引用的是当前的对象实例(即不能在静态成员中使用 this 关键字,因为静态成员不是对象实例的一部分)。this 的第一个常见用法是把当前对象实例的引用传递给一个方法,第二个常见用法是限定局部类型的成员。
结构和类非常相似,但结构是值类型,类是引用类型。
接口就是把公共实例(非静态)方法和属性组合起来,以封装特定功能的一个集合。一旦定义了接口,就可以在类中实现它。接口不能单独存在,不能像实例化一个类那样实例化接口,接口不能包含实现其成员的任何代码,只能定义成员本身。实现过程必须在实现接口的类中完成。接口的定义如下:
interface IMyInterface //接口名一般以大写字母I开头
{
//code
}
访问修饰符关键字有 internal 和 public 两个。与类继承不同的是,一个类可以实现多个基接口。
如果我们想不再需要某个对象时,就释放这个资源,则这个类必须要实现IDisposable接口中的 Dispose() 方法。C#允许使用一种可以优化使用这个方法的结构。using 关键字可以在代码块中初始化使用重要资源的对象,在这个代码块的末尾会自动调用 Dispose() 方法,用法如下:
= new ();
using ()
{
...
}
//或者把初始化对象作为 using 语句的一部分:
using ( = new ())
{
...
}
继承是一个类定义派生于另一个类定义的机制。在OOP中,被继承的类称为父类(也称为基类)。C#中的对象仅能直接派生于一个基类,当然基类也可以有自己的基类。基类还可以定义为抽象类,抽象类不能直接实例化。要使用抽象类,必须继承这个类,抽象类可以有抽象成员,这些成员在基类中没有实现代码,所以派生类必须实现它们。在继承一个基类时,成员的可访问性就成了一个重要问题。派生类可以访问基类的公共成员和受保护的成员(外部代码不能访问),但不能访问其私有成员。密封(seal)的类不能用作基类。在C#中,所有对象都有一个共同的基类 object (在 .NET Framework 中,它是 System.Object 类的别名)。
继承的一个结果就是派生于基类的类在方法和属性上有一定的重叠,因此,可以使用相同的语法处理从同一个基类实例化的不同对象。多态性允许把某个派生类型的变量赋给基本类型的变量,之后就可以通过这个变量调用基类的方法(实际上是调用这个派生类中同名方法的实现代码)。还可以把基本类型的变量转换为派生类变量,调用派生类的方法。实现多态的方法有覆盖(运行期确定),重载(编译期确定)。
在继承层次结构中,所有类的根都是 System.Object。System.Object 包含的方法在这里不再列出。
每个对象都有一个明确定义的生命周期,除了“正在使用”的正常状态外,还有两个重要的阶段:
在C#中定义类时,常常不需要定义相关的构造函数和析构函数,因为在编写代码时,如果没有提供它们,编译器会自动添加它们。但如有必要,可以提供自己的构造函数和析构函数,以便初始化对象和清理对象。
class MyClass
{
public MyClass()
{
//Default constructor code
}
~MyClass() //声明析构函数
{
//code
}
}
所有的类定义至少包含一个构造函数,构造函数分为默认构造函数(没有参数)和非默认构造函数(带参数)。构造函数与字段,属性和方法一样,可以是公共或私有的,在类外部的代码不能使用私有构造函数实例化对象,而必须使用公共构造函数。一些类没有公共的构造函数,外部的代码就不可能实例化它们,这些类称为不可创建的类(静态类也是不可创建的类)。
在C#中,任何构造函数都可以配置为在执行自己的代码前调用其他构造函数。为了实例化派生的类,必须实例化它的基类,而要实例化这个基类,又必须实例化这个基类的基类,这样一直实例化到System.Object
为止。结果是无论使用什么构造函数实例化一个类,总是首先调用System.Object.Object()
。无论在派生类上使用什么构造函数(默认的或非默认的),除非明确指定,否则就使用基类的默认构造函数。只需使用构造函数初始化器,就可以指定.NET 实例化过程使用基类中具有指定参数的构造函数。也可以使用 base 这个关键字指定基类构造函数的字面值。除了 base 关键字外,还可将另一个关键字 this 用作构造函数初始化器。注意在定义构造函数时,不要创建无限循环。例如:
public class MyBaseClass
{
public MyBaseClass() : this(5)
{
}
public MyBaseClass(int i) : this()
{
}
}
此时,编译器会报错:构造函数“MyBaseClass.MyBaseClass(int)”无法通过另一构造函数调用自身。
相同点:都包含可以由派生类继承的成员;都不能直接实例化;派生类必须实现未实现的方法。
不同点:派生类只能直接继承自一个抽象类,类可以使用任意多个接口,但这并不会产生太大的区别;抽象类的成员可以是私有的,受保护的,内部的或受保护的内部成员,而接口成员必须是公共的;接口不能包含字段,构造函数,析构函数,静态成员或常量;抽象类可以拥有抽象成员(没有代码体,必须在派生类中实现,否则派生类本身必须也是抽象的)和非抽象成员(拥有代码体,也可以是虚拟的,这样就可以在派生类中重写),而接口成员必须都在使用接口的类上实现(它们没有代码体)。
什么场景使用什么技术?
抽象类主要用作对象系列的基类,这些对象共享某些主要特性,例如共同的目的和结构。接口则主要用于类,这些类存在根本性的区别,但仍可以完成某些相同的任务。