封装、继承、多态是面向对象编程的三个重要特性
封装
封装是将数据和行为相结合,通过行为约束代码修改数据的程度(公有、私有、受保护),增强数据的安全性,属性是C#封装实现的最好体现。
定义:按照某种逻辑关系,把一些字段、属性、方法封装为一个类,把数据相关的操作封装为不同的方法,是模块化的基础
通过访问级别修饰符,在类、成员之间进行数据隔离
访问级别修饰符
public:对任何类和成员都公开,无限制访问
private:仅对该类公开
protected:对该类和其派生类公开
internal:只能在包含该类的程序集中访问该类
internal 内部的 修饰类或成员
程序集指的是以 .exe 或 .dll 为后缀名的文件
本文末尾的示例程序中,有 Hello.Model.dll 和 testApp.dll 两个程序集
public 公有的 修饰类或成员
公有的类或成员,其他程序中可以自由访问
protected 受保护的 修饰成员
在类的派生类(子类)中,通过派生类对象或在派生类内部,可以访问基类(父类)的受保护成员
在派生类中通过实例化得到的基类对象,不能访问基类中的受保护成员
private 私有的 修饰成员
只能通过类的成员方法来访问
继承
提高代码重用度,增强软件可维护性的重要手段,符合开闭原则。
使用 继承 来描述类与类之间的亲缘关系,提高代码重用度
C# 是单继承、多接口的
声明一个类并继承其他类的写法 public class B : A {}
继承一个或多个接口 public class B:A,IClickHandler,IDragHandler
接口和抽象类
-
接口不能实例化 抽象类可以间接实例化
-
接口是完全抽象 抽象类为部分抽象
-
接口可以多继承 抽象类只能单继承
常用关键字
interface 声明接口
abstract 声明抽象类
sealed 类声明时可防止其他类继承此类,在方法中声明则可防止派生类重写此方法。
多态
多态性是指同名的方法在不同环境下,自适应的反应出不同得表现,是方法动态展示的重要手段。
同名方法的重载和重写
-
所处位置不同 重载在同类中 重写在父子类中
-
定义方式不同 重载方法名相同 参数列表不同 重写方法名和参数列表都相同
-
调用方式不同 重载使用相同对象以不同参数调用 重写用不同对象以相同参数调用
-
多态时机不同 重载时编译时多态 重写是运行时多态
操作符重载
重载操作符 () ,将苹果类转化为水果类,使用关键字 explicit operator
class Apple
{
public int price = 10;
public static void Main()
{
Apple apple = new Apple();
Console.WriteLine(apple.price);
// 重载()操作符
Fruit a = (Fruit)apple;
Console.WriteLine(a.name);
}
// 操作符重载
public static explicit operator Fruit(Apple apple)
{
Fruit f = new Fruit();
f.name = "苹果";
return f;
}
}
class Fruit
{
public string name = "默认名字";
}
常用关键字
virtual 表示该方法可以被派生类重写
override 表示重写父类的方法
base 表示父类对象
示例程序
创建解决方案
dotnet new sln -o my_solution1
创建类库
dotnet new classlib -o Hello.Model
创建命令行程序
dotnet new console -o testApp
把类库和命令行程序加入解决方案中
dotnet sln add ./Hello.Model/Hello.Model.csproj ./testApp/testApp.csproj
在命令行程序目录下,添加对类库的引用
dotnet add reference ../Hello.Model/Hello.Model.csproj
直接在命令行程序目录下运行程序 dotnet run
在解决方案的目录下运行某个工程 dotnet run --project testApp
目录结构
- my_solution1.sln 解决方案文件
- testApp 命令行程序目录
- testApp.csproj
- Program.cs
- Hello.Model 类库目录
- Hello.Model.csproj
- Class1.cs
代码
Class1.cs 类库
namespace Hello.Model
{
public class Class1
{
internal int number = 0;
internal static int number2 = 2;
public void PublicHello()
{
Console.WriteLine("PublicHello start");
ProtectedHello();
PrivateHello();
Console.WriteLine("PublicHello end");
}
protected void ProtectedHello()
{
Console.WriteLine("ProtectedHello");
}
// 成员的默认访问级别是 private
private void PrivateHello()
{
Console.WriteLine("PrivateHello");
}
}
// 类的默认访问级别是 internal
internal class InternalClass
{
public static int number = 0;
}
public class A
{
private int numberA_p = 3;
protected int numberA = 1;
public void logA()
{
// 3.3 在类内部是可以访问 protected 成员的
Console.WriteLine("logA " + numberA);
}
public virtual void vlog()
{
}
}
// 继承
public class B : A
{
public void log()
{
A a = new A();
// 3.2 无法在子类中通过实例化父类对象,访问父类的受保护成员 报错 CS1540
// a.numberA = 2;
Console.WriteLine("B class log " + numberA);
// 4.1 无法访问基类(父类)的私有成员 报错 CS0122
// Console.WriteLine(numberA_p);
}
// 重载 log 方法
public void log(string s)
{
Console.WriteLine(s);
}
// 重写 父类的 vlog 方法
public override void vlog()
{
// 调用父类的同名方法
base.vlog();
}
}
}
Program.cs 命令行主程序
// 引用类库
using Hello.Model;
// 1.1 跨文件 无法访问到 internal 修饰的类 报错 CS0122
// InternalClass c = new InternalClass();
// 1.2 跨文件 能实例化 public 修饰的类
Class1 c1 = new Class1();
// 1.3 但是无法访问到 Class1 类中 internal 修饰的成员 报错 CS1061
// Console.WriteLine(c1.number);
// 1.4 也无法访问到 internal static 修饰的成员 报错 CS0117
// Console.WriteLine(Class1.number2);
// 2.2 public 修饰的方法可以访问
// PublicHello start
// ProtectedHello
// PrivateHello
// PublicHello end
c1.PublicHello();
// 3.2 父类对象在类外部不能访问受保护的方法 报错 CS0122
// c1.ProtectedHello();
// 4.1 父类对象在类外部不能访问自身的私有方法 报错 CS1061
// c1.PrivateHello();
// 2.1 public 修饰的类可以访问
A a = new A();
// 2.2 public 修饰的成员可以访问
// logA 1
a.logA();
B b = new B();
// 3.1 在派生类中可以访问基类的 protected 修饰的成员
// B class log 1
b.log();