目录
1.封装
封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
C# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。
一个 访问修饰符 定义了一个类成员的范围和可见性。C# 支持的访问修饰符如下所示:
- public:所有对象都可以访问;
- private:对象本身在对象内部可以访问;
- protected:只有该类对象及其子类对象可以访问
- internal:同一个程序集的对象可以访问;
- protected internal:访问限于当前程序集或派生自包含类的类型。
2.继承
派生类格式
属性 类修饰符 class 派生类名:基类名{
类体
}
子类不能继承基类中定义的private方法,只能继承基类的public成员或者protected成员。
基类的初始化会首先调用基类的构造函数再调用子类的构造函数,子类中如果有有参的构造函数,会默认调用父类中无参的构造函数,此时,要在父类中定义一个无参的构造函数。如果想调用父类中有参的构造函数,要在子类中构造函数后面加":base(继承父类的参数)",同时,在子类中构造函数中写自己独有的属性即可。
如果子类中要写一个函数覆盖父类中的某个函数,则在定义时用new,不能用override.
class Program
{
static void Main(string[] args)
{
employee employe = new employee("党小春",25,"福德村6组",1000);
employe.Display();
employee employe2 = new employee();
employe2.Display();
//Console.WriteLine(employe.Age);
Console.ReadKey();
}
}
public class Person
{
public string Name;
public int Age;
public Person(string name,int age)
{
Name = name;
Age = age;
}
public Person()
{
}
public void Display()
{
Console.WriteLine("姓名:{0} 年龄:{1}", Name, Age);
}
}
class employee : Person //Person是基类
{
private string department;
private decimal salary;
public employee():base()
{
}
public employee(string name, int age, string depmt, decimal sal)
: base(name, age)
{//base的第一种用法,根据参数调用指定基类构造函数,注意参数的传递
department = depmt;
salary = sal;
}
public new void Display()
{//覆盖基类Display()方法,注意new,不可用override
base.Display(); //base的第二种用法,访问基类被覆盖的方法
Console.WriteLine("部门:{0} 薪水:{1}", department, salary);
}
}
若B继承自A,也可以使用强制转换操作将其转换为A 对象。如:
A b = (A)new B();或者 A b = new B();此时,B对象将被限制为A 对象的数据和行为,而无法再访问B对象中的数据和行为,除非A中的方法被B重写,将会访问B 的方法。将B强制转换为A后,还可以将A重新转换为B,但是,并非A的所有实例在任何情况下都可强制转换为B,只有实际上是B的实例的那些实例才可以强制转换为B。
如下所示,满足上述文字描述的
static void Main(string[] args)
{
Person employe = new employee("党小春",25,"福德村6组",1000);
employe.Display();
employee e = (employee)employe;
e.Display();
//employe.Display();
//employee employe2 = new employee();
//employe2.Display();
Console.WriteLine(employe.Age);
Console.ReadKey();
}
3.多态
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。实际上就是同一个类型的实例调用“相同”的方法,产生的结果是不同的。这里的“相同”打上双引号是因为这里的相同的方法仅仅是看上去相同的方法,实际上它们调用的方法是不同的。
多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。
1.静态多态性
在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C# 提供了两种技术来实现静态多态性。分别为:
1.函数重载
在同一个作用域(一般指一个类)的两个或多个方法函数名相同,参数列表不同的方法叫做重载,它们有三个特点(俗称两必须一可以):
- 方法名必须相同
- 参数列表必须不相同
- 返回值类型可以不相同
2.运算符重载
重载运算符是具有特殊名称的函数,是通过关键字 operator 后跟运算符的符号来定义的。与其他函数一样,重载运算符有返回类型和参数列表。
用户自定义的操作符要写public和static
class Program
{
static void Main(string[] args)
{
Box a = new Box(2, 3.1);
Box b = new Box(3, 5.4);
Box c = a + b;
Console.WriteLine("c.length={0},c.width={1}", c.length, c.width);
Console.ReadKey();
}
}
}
public class Box
{
public int length;
public double width;
public Box()
{
}
public Box(int len,double wid)
{
length = len;
width = wid;
}
public static Box operator+(Box a,Box b)
{
Box box = new Box();
box.length = a.length + b.length;
box.width = a.width + b.width;
return box;
}
}
3.可重载和不可重载运算符
下表描述了 C# 中运算符重载的能力:
运算符 | 描述 |
---|---|
+, -, !, ~, ++, -- | 这些一元运算符只有一个操作数,且可以被重载。 |
+, -, *, /, % | 这些二元运算符带有两个操作数,且可以被重载。 |
==, !=, <, >, <=, >= | 这些比较运算符可以被重载。 |
&&, || | 这些条件逻辑运算符不能被直接重载。 |
+=, -=, *=, /=, %= | 这些赋值运算符不能被重载。 |
=, ., ?:, ->, new, is, sizeof, typeof | 这些运算符不能被重载。 |
2.动态多态性
动态多态性是通过 抽象类 和 虚方法 实现的。
1.虚拟(virtual)和重写(override)
c#中,如果基类的某个功能不满足要求,可以在子类中重写基类的方法实现新的功能,子类中为满足自己的需要来重复定义某个方法的不同实现,需要用override关键字,被重写的方法必须是虚方法,用的是virtual关键字。它的特点是(三个相同):
- 相同的方法名
- 相同的参数列表
- 相同的返回值。
class Program
{
static void Main(string[] args)
{
employee e = new employee();
e.Display();
Console.ReadKey();
}
}
public class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
public Person()
{
}
public virtual void Display()
{
Console.WriteLine("姓名:{0} 年龄:{1}", Name, Age);
}
}
class employee : Person //Person是基类
{
private string department;
private decimal salary;
public employee()
: base()
{
}
public employee(string name, int age, string depmt, decimal sal)
: base(name, age)
{//base的第一种用法,根据参数调用指定基类构造函数,注意参数的传递
department = depmt;
salary = sal;
}
/// <summary>
/// 重写Display方法
/// </summary>
public override void Display()
{//覆盖基类Display()方法,注意new,不可用override
//base.Display(); //base的第二种用法,访问基类被覆盖的方法
Console.WriteLine("部门:{0} 薪水:{1}", department, salary);
}
}
注意:(1)虚拟方法不能声明 为静态的。因为静态的方法是应用在类一层次的,而面向对象的多态性只能在对象上运作,所以无法在类中使用。此外,override,abstract也不能跟static共存.
(2)virtual 不能和private 一起使用,否则无法在子类中重写。
(3)重写方法的名称、参数个数、类型以及返回值都必须和虚拟方法一致。
2.隐藏
只要在子类的方法或属性前声明为new,就可以隐藏基类的方法或属性。理解:新方法把老方法(相对于子类)隐藏,但老方法仍可见。
注意:(1)隐藏方法不但可以隐藏基类中的虚方法,而且也可以隐藏基类中的非虚方法。
(2)隐藏方法中父类的实例调用父类的方法,子类的实例调用子类的方法。
(3)和上一条对比:重写方法中子类的变量调用子类重写的方法,父类的变量要看这个父类引用的是子类的实例还是本身的实例,如果引用的是父类的实例那么调用基类的方法,如果引用的是派生类的实例则调用派生类的方法。
什么时候用隐藏呢:如开发人员A需要重新设计基类中的某个方法,该基类是一年前由另一组开发人员设计的,并且已经交给用户使用,可是原来的开发人员在方法前没有加virtual关键字,但又不允许修改原来的程序,这是无法用override重写基类的方法,这是就需要隐藏基类的方法。
3.抽象类
若类中的方法或属性为abstract,类必须用abstract修饰。只能用作基类,抽象方法没有实现代码(无大括号!)
class Program
{
static void Main(string[] args)
{
B b = new B();
b.print();
Console.ReadKey();
}
}
public abstract class A
{
public abstract void print();
}
public class B : A
{
public override void print()
{
Console.WriteLine("print!");
}
}
当从抽象类派生非抽象类时,非抽象类必须实现抽象类的所有(必须是所有!)抽象成员,当然,如果继承抽象类的也是抽象类就不必实现其抽象成员,由最后一个非抽象类实现。
抽象类的一些规则:
- 您不能创建一个抽象类的实例。
- 您不能在一个抽象类外部声明一个抽象方法。
- 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
4.接口
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。
接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。接口使得实现接口的类或结构在形式上保持一致。
interface IMyInterface
{
void MethodToImplement();
}
如果一个接口继承其他接口,那么实现类或结构就需要实现所有接口的成员。
参考
https://www.cnblogs.com/HansenChen/archive/2012/03/09/2388247.html
https://www.cnblogs.com/zhangkai2237/archive/2012/12/20/2826734.html