单一职责原则(对象职责明确原则)
要求:一个对象制作好一件事情,必须专注,职责过多容易引起变化的原因就多,程序就不稳定
开发封闭原则(核心原则)
要求:需求变化是尽量少的修改类的设计,而是通过扩展类来完成。即封闭修改、开放扩展
依赖倒置原则(OOP精髓)
要求:基于接口编程,高层模块调用接口,底层模块实现接口,防止底层变化直接影响高层
接口隔离有原则
要求:尽可能多的使用专用的小接口,而不是总接口,避免接口过于复杂
里氏转换原则
要求:在继承关系中子类可以替换父类,虚拟机可以根据父类变量动态的找到具体的子类对象,从而实现多态
C# 中面向对象编程的特征包括以下几个方面:
封装是将数据和方法绑定在一起的机制,以防止外部访问对象的内部数据,并确保数据的完整性。通过封装,对象的内部状态被隐藏,只有特定的方法可以访问和修改对象的状态,从而提高了代码的安全性和可维护性。
隐藏内部实现细节,模块开发者只关心内部实现和接口要求。模块调用者只关心接口调用
好处:安全保证(避免代码外漏)、快速应用(直接应用)、团队协作(多人同时工作)
应用:类是程序的基本单元,也是封装的基本单元,但是类可以同样被模块封装
封装是面向对象编程中的一个重要概念,它允许将数据和方法捆绑在一起,形成一个独立的单元,同时隐藏对象内部的实现细节。在C#中,封装主要通过访问修饰符和属性(Properties)、方法(Methods)、字段(Fields)等来实现。以下是C#中封装的一些重要方面:
1.访问修饰符(Access Modifiers):
C#提供了不同的访问修饰符,用于控制类的成员对外的可见性。
常见的访问修饰符包括:
属性是一种特殊的方法,用于读取或写入对象的私有字段。通过使用属性,可以在封装的同时提供对对象状态的控制。以下是一个简单的属性的示例:
private int _age;
public int Age
{
get { return _age; }
set
{
if (value >= 0)
_age = value;
else
throw new ArgumentException("年龄不能为负数");
}
}
对象的动态特征就是方法,方法表示这个对能能够做什么
实例方法、静态方法、(构造方法、抽象方法、虚方法)
封装还涉及到将一组相关的操作封装在方法中。通过方法,可以对对象进行操作,而不必直接访问其内部状态。方法的访问修饰符同样用于控制外部代码对方法的可见性。
注意事项:
访问修饰符:可以省略,默认private,可以需要根据定义成public
方法名:一般是“动词”或“动宾短语”,采用Pascal命名法首字母大写,不能以数字开头
参数列表:根据需要添加,也可以省略。参数列表要求“类型 参数名”
有返回值:使用return返回,return语句后面不能再有其他语句
没有返回值:如果不返回任何数据,使用void表示。
1、我们在Main()函数中,调用Test()函数,我们管Main()函数称之为调用者,
管Test()函数称之为被调用者。 如果被调用者想要得到调用者的值:
private int _age;
public int Age
{
get { return _age; }
set
{
if (value >= 0)
_age = value;
else
throw new ArgumentException("年龄不能为负数");
}
}
class Program
{
public static int _number = 10;//静态字段
static void Main(string[] args)
{
int b = 10;
int a = 3;
int res = Test(a);
Console.WriteLine(res);
Console.WriteLine(_number);
Console.ReadKey();
}
public static int Test(int a)
{
a = a + 5;
return a;
}
}
如果调用者想要得到被调用者的值: 1)、返回值
2、不管是实参还是形参,都是在内存中开辟了空间的。
public static void Main(string[] args)
{
//比较两个数组的大小 返回最大的
int num = GetMax(10, 20);//传递的实参
Console.WriteLine(num);
Console.ReadKey();
}
///
/// 计算两个整数之间的最大值
///
/// 第一个参数
/// 第二个参数
/// 返回的最大值
public static int GetMax(int n1,int n2)//形参
{
int max = n1 > n2 ? n1 : n2;
return max;
}
字段是用于存储对象数据的成员,通常应该是私有的,以确保只有通过类的公共接口来访问。访问字段的方法可以通过公共的属性来提供,以实现对字段的封装。
总体来说,通过使用适当的访问修饰符、属性、方法和字段的组合,C#中的封装机制使得对象的内部实现细节能够被隐藏,同时通过公共接口提供对对象的控制和访问。这有助于提高代码的安全性、可维护性和可扩展性。
继承是一种机制,允许一个类(子类)基于另一个类(父类)的定义来创建。子类可以继承父类的属性和方法,并且可以扩展或修改它们。继承提供了代码重用的机制,使得在开发过程中可以构建层次化的类结构。
复用现有代码
好处:一处更新,处处更新;
弊端:关联容易复杂
应用:object、Form.....
我们可能会在一些类中,写一些重复的成员,我们可以将这些重复的成员, 单独的封装到一个类中,作为这些类的父类。 Student、Teacher、Driver 子类 派生类 Person 父类 基类 子类继承了父类,那么子类从父类那里继承过来了什么?
首先,子类继承了父类的属性和方法,但是子类并没有继承父类的私有字段。
问题:子类有没有继承父类的构造函数? 答:子类并没有继承父类的构造函数,但是。
子类会默认的调用父类无参数的构造函数, 创建父类对象,让子类可以使用父类中的成员。 所以,如果在父类中重新写了一个有参数的构造函数之后,那个无参数的就被干掉了, 子类就调用不到了,所以子类会报错。
解决办法: 1)、在父类中重新写一个无参数的构造函数。
2)、在子类中显示的调用父类的构造函数,使用关键字:base()
1、继承的单根性:一个子类只能有一个父类。
2、继承的传递性
多态是指相同的消息可以被不同类型的对象响应,即一个方法调用可以根据调用对象的不同而执行不同的行为。
让一个对象的接口可以根据不同的请求,做成不同的响应
好处:程序扩展容易、维护方便
应用:继承多态、接口多态(简单工厂、抽象工厂)
多态性通过方法重载(Overloading)、方法重写(Override)和接口实现(Interface Implementation)等方式实现。多态性提高了代码的灵活性和可扩展性,使得代码可以更容易地应对不同的需求和变化。
在C#中,多态性是面向对象编程中的一个重要概念,它允许不同类的对象对同一消息做出响应,表现出不同的行为。多态性通过继承和重载(接口)实现来实现。
1.继承多态性:通过继承,子类可以重写父类的虚方法,从而实现多态性。当调用父类的方法时,实际上会根据对象的实际类型调用对应的子类方法。
class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal makes a sound");
}
}
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Dog barks");
}
}
class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Cat meows");
}
}
Animal animal = new Dog();
animal.MakeSound(); // 输出: Dog barks
animal = new Cat();
animal.MakeSound(); // 输出: Cat meows
2.接口多态性:通过接口,不同的类可以实现相同的接口,并实现接口定义的方法。然后,可以通过接口变量来调用不同类的方法,实现多态性。
interface IShape
{
double CalculateArea();
}
class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
public double CalculateArea()
{
return Width * Height;
}
}
class Circle : IShape
{
public double Radius { get; set; }
public double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
IShape shape = new Rectangle { Width = 5, Height = 10 };
Console.WriteLine($"Rectangle area: {shape.CalculateArea()}");
shape = new Circle { Radius = 5 };
Console.WriteLine($"Circle area: {shape.CalculateArea()}");
多态性使得代码更加灵活和可扩展,可以通过修改现有的类或者添加新的类来改变程序的行为,而不需要修改调用方的代码。
步骤: 1、将父类的方法标记为虚方法 ,使用关键字 virtual,这个函数可以被子类重新写一个遍。
static void Main(string[] args)
{
//打卡练习
Employ e = new Employ();
Manager m = new Manager();
Programmer p = new Programmer();
Employ[] em = { e, m, p };
for(int i = 0; i < em.Length; i++)
{
em[i].DaKa();
}
Console.ReadKey();
}
public class Employ
{
public virtual void DaKa()
{
Console.WriteLine("员工九点打卡");
}
}
public class Manager : Employ
{
public override void DaKa()
{
Console.WriteLine("经理11点打卡");
}
}
public class Programmer : Employ
{
public override void DaKa()
{
Console.WriteLine("码农不打卡");
}
}
抽象是隐藏对象的复杂性,并只显示所需的特征和行为的过程。抽象通过抽象类(Abstract Class)和接口(Interface)来实现。抽象类是一种不能被实例化的类,它定义了一组抽象方法和/或属性,需要由子类来实现。接口定义了一组方法和属性的契约,实现接口的类必须提供这些方法和属性的具体实现。抽象提供了代码重用和规范化的机制,使得代码更易于理解和维护。
当父类中的方法不知道如何去实现的时候,可以考虑将父类写成抽象类,将方法写成抽象方法。
1.抽象成员必须标记为abstract,并且不能有任何实现。 2.抽象成员必须在抽象类中。 3.抽象类不能被实例化
internal class Program
{
static void Main(string[] args)
{
/*
泛型集合
*List
*Dictionary
*装箱:将值类型转换为引用类型
*拆箱:将引用类型转化为值类型
*应该尽量避免在代码中发生装箱或者拆箱
*文件流
*FileStream StreamReader和StreamWriter
*多态:虚方法、抽象类、接口
*/
//虚方法和抽象类
//老师可以起立 学生也可以起立
//声明父类去指定子类对象
Person p = new Student();//new Teacher new HeadMaster
p.Standup();
Console.ReadKey();
}
}
public abstract class Person
{
public abstract void Standup();
}
public class Student : Person
{
public override void Standup()
{
Console.WriteLine("老师起立,校长好");
}
}
public class Teacher : Person
{
public override void Standup()
{
Console.WriteLine("老师起立,校长好");
}
}
public class HeadMaster : Person
{
public override void Standup()
{
Console.WriteLine("老师起立,校长好");
}
}
面向对象编程的一个主要优点是代码的重用性。通过继承和组合等机制,可以在不同的类之间共享和重用代码,从而减少重复编写代码的工作量,并提高代码的可维护性和可扩展性。
这些特征共同构成了面向对象编程的基础,使得C#等面向对象编程语言能够更好地组织和管理复杂的软件系统。
方法的重载指的就是方法的名称相同给,但是参数不同。 参数不同,分为两种情况 1)、如果参数的个数相同,那么参数的类型就不能相同。 2)、如果参数的类型相同,那么参数的个数就不能相同。 ***方法的重载跟返回值没有关系。
public class Func
{
// 1、显示给出默认的构造函数,这点与结构体不同,结构体的默认构造不能写,一直存在。
public Func()
{
}
// 2、带一个 int 类型参数的自定义构造
public Func(int a)
{
}
// 3、 与 2 不同,带一个 string 类型参数的自定义构造方法
public Func(string a)
{
}
// 4、与 2,3都不同,有两个参数
public Func(int a,int b)
{
}
// 注意下面这种情况,光是返回值不同不是方法重载,编译器也会报错,所以一定要参数列表不同
public int GetNum()
{
return 0;
}
public void GetNum(int a)
{
}
}
在C#中,虚方法()和抽象方法()都用于实现多态性(Polymorphism),但它们之间有一些重要的区别。virtual``abstract
相同点:
都可以被派生类重写(override)
abstract和virtual两个关键字都是用来修饰基类的,
abstract可以修饰类,即抽象类咯,也可以修饰方法,即抽象方法咯;
virtual只能修饰方法,即虚方法。
派生类可以通过覆盖其基类里面abstract修饰的抽象方法或者virtual修饰的虚方法,去重写两种方法。
默认实现:
虚方法():可以在基类中提供默认实现,子类可以选择性地覆盖()它,也可以直接使用基类的实现。virtual``override
抽象方法():没有默认实现,子类必须提供具体实现。abstract
使用场景:
虚方法():通常用于在基类中提供通用实现,但允许子类根据需要进行覆盖,实现特定的行为。如果在基类中没有提供默认实现,子类也可以选择不覆盖虚方法。virtual
抽象方法():通常用于定义一个抽象的行为或契约,但不提供具体实现。抽象方法必须在子类中实现,以确保每个子类都提供了适当的行为。abstract
使用方式:
虚方法():使用 关键字在方法声明中标记虚方法。virtual``virtual
抽象方法():使用 关键字在方法声明中标记抽象方法,而且所在的类必须是抽象类(使用 关键字声明的类)或接口(接口中的方法默认为抽象的)。abstract``abstract``abstract
继承关系:
虚方法():可以被子类继承,并且子类可以选择性地覆盖它。virtual
抽象方法():必须被子类继承并实现,否则子类也必须声明为抽象类。abstract
总之,虚方法提供了一种在基类中提供默认实现但允许子类进行覆盖的机制,而抽象方法则强制子类必须提供具体实现。
在 C# 中,重载(Overloading)和重写(Overriding)是两个不同的概念,用于处理方法的多态性,但它们有不同的作用和使用场景。
重载(Overloading):
定义: 重载是指在同一个类中,可以定义多个具有相同名称但参数列表不同的方法。 区分标志: 方法的区分标志是参数的类型、数量或者顺序。 编译时解析: 编译器在编译时会根据调用的方法参数的类型、数量或者顺序来选择正确的重载方法。 例子:
class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
}
重写(Overriding):
定义: 重写是指在派生类中,可以重新实现基类中定义的虚拟方法(使用 virtual 关键字标记的方法)。 要求: 重写方法的签名(方法名、参数类型和返回类型)必须与基类中的虚方法一致。 运行时解析: 在运行时,根据对象的实际类型(而不是变量的声明类型)来确定调用的方法。 使用关键字: 使用 override 关键字标记重写的方法。 例子:
class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Generic animal sound");
}
}
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}
总结:
重载(Overloading) 是在同一个类中定义多个同名方法,通过参数的类型、数量或顺序进行区分,是编译时多态性。 重写(Overriding) 是在派生类中重新实现基类中的虚方法,根据对象的实际类型来调用正确的方法,是运行时多态性。