C#面向对象——封装
封装:封装是将数据和行为相结合。通过行为约束代码修改数据的程度,增强数据的安全性,意义在于保护或防止代码(数据)被外面无意中修改。
1.类
概念:具有相同特征和行为的一类事物的抽象。类是对象的模块,可以通过类创建对象。
类的关键字:class
// class 类名(帕斯卡命名法)
{
// 成员变量
// 成员属性
// 构造函数
// 析构函数
// 成员方法
// 索引器
// 运算符重载
// 静态成员和静态成员方法
}
1.类对象
1.基本概念
类的声明和类对象的声明是两个概念。类对象是类的具体实例。
类对象的声明相当于声明了一个自定义变量类型。
类对象是引用类型。
2.实例化对象的基本语法
// 类名 类对象名
// 类名 类对象名 = null;
// 类名 类对象名 = new 类名();
class Person
{
// 成员变量(若没有写访问修饰符,则默认为private 私有类型)
public string name;
public int age;
}
//类的实例化
Person p; //仅声明了Person类型的变量,未初始化
Person p2 = null; // 初始化为null
Person p3 = new Person(); // 对象实例化
3.实例
class Person
{
public string name;
public int age;
}
static void Main()
{
// 练习一
Person A = new Person();
Person B = A; // 拷贝A的地址。此时如果B改变A也会改变
B = null; // 此时B为null,A仍然存在
// 练习二
Person A = new Person();
Person B = A; // 拷贝A地址
B = new Person(); // 此时A与B之间没有任何关系了
}
2.成员变量
1.基本概念
声明在类语句块中,用来描述对象的特征,可以是任意变量类型,数量不做限制。
2.基本语法
注意:
1.类中的成员变量声明时可以初始化;结构体中的成员变量声明时不能初始化(因为结构体是值类型),必须通过构造函数初始化。
原因:值类型声明时就有默认值,额外的初始化会导致语义混乱
2.值类型未初始化,数值类型默认为0,布尔类型默认为false;引用类型未初始化,默认为Null。
class Person
{
// 访问修饰符 变量类型 变量名;
public string name;
public int age;
public Person p;
// 注意:类中声明了一个与自己相同类型的成员变量时,不能直接初始化(否则会导致无限递归)
}
3.成员属性
1.基本概念
用于保存成员变量,为成员变量提供了更灵活、更安全的数据访问方式。解决了3P(public、private、protected)访问修饰符的局限性。
属性可以让成员变量在外部只能获取不能修改或者只能修改不能获取。
2.基本语法
// 访问修饰符 属性类型 属性名
// {
// get{读,获取数据逻辑}
// set{取,修改数据逻辑}
// }
注意:
1.成员属性get和set前可以添加访问修饰符,如果不加,默认使用属性声明时的访问修饰符
2.添加修饰符要低于属性的访问修饰符,但是不能让get和set的访问权限都低于成员属性。
public class Person
{
private string name;
public string Name
{
get{return name;}
// value 关键字,代表外面传入的值
set{name = value;}
}
}
static void Main()
{
// 实例化对象
Person p = new Person();
p.Name = "张三";
Console.WriteLine(P.Name); // 输出:张三
}
3.自动属性
当在get和set逻辑中不做如何处理时可以使用。
public int Age {get; set; } = 18;
// 等效于
// private int <Age>k__BackingField = 18;
// public int Age { get; set; }
4.构造函数
1.基本概念
构造函数 是类的一个特殊的成员函数,当创建类的新对象时执行。构造函数的名称与类的名称完全相同,它没有任何返回类型。它在创建类的新实例时自动调用。
在实例化对象时,会调用用于初始化的构造函数,如果不写,默认存在一个无参构造函数。
2.基本语法
1.没有返回值
2.函数名与类名相同
3.没有特殊需求一般为public
4.构造函数可以被重载
5.this代表当前被调用该函数对象自己
注意:如果重载了构造函数(有参构造函数),会默认失去默认的无参构造函数
class Myclass
{
public string name
publuc int age;
// 无参构造函数
// 编译器自动生成
public Myclass(){}
// 有了有参构造函数,会失去默认的无参构造函数
public Myclass(string name, int age)
{
this.name = name; // this.name 是指类中的成员变量name
// name 是指构造函数传入的参数name
this.age = age;
}
}
3.构造函数的特殊写法(构造函数链式调用)
构造函数链式调用(: this(...))只能传递 具体值 或 变量
class Myclass
{
public string name;
public int age;
public string className;
public Myclass(string name, int age, string className)
{
this.name = name;
this.age = age;
this.className = className;
}
// 先执行构造函数1,再执行构造函数2
public Myclass(string str, int value): this(str, value, "一年一班")
{
Console.WriteLine("构造函数2");
}
// 先执行构造函数2,再执行构造函数3
public Myclass(string str2): this(str2, 10)
{
Console.WriteLine("构造函数3");
}
}
5.析构函数
1.基本概念
析构函数(Finalizer)是一种特殊的方法,用于在对象被垃圾回收器回收之前执行清理操作。与 C++ 不同,C# 的析构函数不是确定性的,其执行时间由垃圾回收器(GC)决定。
当引用类型的堆内存被回收时,会调用这个函数。对于需要手动管理内存的语言(例如:C#),需要在析构函数中做一些回收处理。
但在C#语言中,存在自动垃圾回收机制GC,不需要手动管理内存,因此析构函数一般不用写,除非你想在某一个对象被垃圾回收时,做一些特殊处理
2.基本语法
不能有访问修饰符,不能有参数
// ~ 类名()
// {
//
// }
3.垃圾回收机制
垃圾回收机制,英文简称GC
垃圾回收过程是在遍历堆上动态分配的所有对象,通过识别它们是否被引用来确定哪些对象是垃圾,哪些对象仍然要被使用。
所谓垃圾就是没有被任何变量/对象引用的内容。
注意:
GC只负责堆内存的垃圾回收。
引用类型存储在堆中的,所以它的分配和释放都是通过垃圾回收机制来管理的。
栈上的内存由系统自动管理的。
值类型存储在栈中,他们有自己的生命周期,不用对他们进行管理,会自动分配和释放。
手动触发垃圾回收的方法:GC.Collect();
一般情况下,我们不会频繁调用,都是在Loading条过场景时才调用。
6.成员方法
1.基本概念
1.成员方法就是类中的方法;
2.是用来描述对象的行为的;
3.规则和函数声明的规则相同;
4.受到访问修饰符规则的影响;
5.方法的数量不做限制
注意:
1.成员方法不要加static
2.成员方法,必须实例化出类对象,再通过对象来使用,相当于该对象执行某个行为
3.成员方法,受访问修饰符的影响
2.基本语法
public class Calculator
{
// 成员变量
public int a;
public int b;
// 成员属性
public int A
{
get a;
set a = value;
}
public int B
{
get b;
set b = value;
}
// 构造函数
public Calcuator(int a, int b)
{
this.a = a;
this.b = b;
}
// 成员方法
public int Add(int a, int b)
{
return a + b;
}
}
7.索引器
1.基本概念
让对象可以像数组一样通过索引访问其中的元素,使程序看起来更加直观,更容易编写。
注意:
索引器也可以重载
结构体中也支持索引器
2.索引器语法
访问修饰符 返回值类型 this[参数类型 参数名]
{
get { /* 返回索引对应的值 */ }
set { /* 设置索引对应的值 */ }
}
class Person
{
private string name;
private int age;
}
class PersonCollection
{
private Person[] person;
public PersonCollection(int index)
{
person = new Person[index];
}
public Person this[int index] // this关键字确实指向 当前 PersonCollection实例
{
get
{
return person[index];
}
set
{
person[index] = value;
}
}
}
static void Main(string[] args)
{
PersonCollection p = new PersonCollection(5);
p[0] = new Person(); // set逻辑
Person person = p[1]; //get逻辑
}
8.静态成员和静态方法
1.基本概念
1.静态关键字:static
用static修饰的成员变量、方法、属性等等称为静态成员、静态方法、静态属性
2.静态成员的特点:直接用类名点出使用(非静态类中的静态成员也可以直接点出来使用)
原因:静态成员属于类型本身,而不是类型的实例
2.基本语法
class Person
{
// 非静态成员变量
// 属于对象(实例化的对象)
public string name;
// 静态成员变量
// 属于Person类型本身
static public int age;
static public void Test()
{
Console.WriteLine("静态成员方法");
}
}
static void Main()
{
Person.age = 10;
person.Test();
}
3.为什么静态成员可以直接点出来使用
程序中是不能无中生有的。我们要使用的对象、变量、函数都需要在内存中分配内存空间的,之所以需要实例化对象,目的就是分配内存空间,在程序中产生一个抽象的对象。而静态成员在程序开始就会分配内存空间。(非静态类在定义时不会分配实例内存,但会立即分配静态成员的内存)
4.静态成员的特点
1.程序开始运行时,就会分配内存空间,所以我们可以直接使用
2.静态成员与程序同生共死
3.只要使用了它,直到程序结束时内存空间才会释放
4.所以应该静态就会有自己唯一的一个“内存小房间”
5.这让静态成员有了唯一性
6.在任何地方都是使用的小房间里的内容,改变了它也就是改变了小房间里的内容
注意:
静态成员的本质就是数据共享,无论你非静态类实例化了多少个对象,非静态类里的静态成员都是同一个
非静态类中可以有静态成员,静态类中不能有非静态成员(生命周期的差异性)
class Person
{
// 非静态成员变量
public string name;
// 静态成员变量
static public int age;
// 常量
public const a = 10;
}
static void Main()
{
Person p1 = new Person();
Person p2 = nerw Person();
p1.age = 20; // 也可以写成 Person.age = 20;
Console.WriteLine(p1.age); // 输出:20
Console.WriteLine(p2.age); // 输出:20
Console.WriteLine(Person.a); // 输出:10
}
5.静态成员对于我们的作用
静态方法,常用于唯一方法的声明
常量可以理解成特殊的静态变量
常量和静态变量的区别:
相同点:他们都可以通过类名点出使用
不同点:1.const(变量)必须初始化,不能修改;static没有这个规则
2.const只能修饰字段(成员变量或局部变量),static可以修饰很多
3.const一定要写在访问修饰符后面,static没有这个要求
9.静态类和静态构造函数
1.基本概念
指通过 static 关键字修饰的类,通常用于包含一组静态成员(如静态方法、静态属性)。静态类无法实例化,也不能继承或被继承,主要用于工具类或全局功能封装。
2.静态类的特点
不可实例化:静态类不能通过 new 关键字创建对象实例。
仅包含静态成员:所有成员(方法、属性、字段)必须为静态。
隐式密封:静态类在编译时会被标记为 sealed,禁止派生(不能被继承,也不能继承其他类)。
public static class StringHelper // 静态工具类
{
// 静态构造函数
static StringHelper()
{
}
public static string Reverse(string input) // 静态方法
{
// 将字符串转为字符数组
char[] chars = input.ToCharArray();
// 反转字符数组
Array.Reverse(chars);
// 将字符数组转回字符串
return new string(chars);
}
}
string str = StringHelper.Reverse("Hello"); // 输出:olleh
10.拓展方法
1.基本概念:
为现有的非静态的变量类型添加新的方法
2.作用
1.提升程序拓展性
2.不需要在对象中重新写方法
3.不需要继承来添加方法
4.为别人封装的类型写额外的方法
3.特点
1.一定是写在静态类中
2.一定是个静态函数
3.第一个参数为扩展对象
4.第一个参数用this修饰
4.基本语法
访问修饰符 static 返回值 函数名(this 拓展类型 参数名,参数类型 参数名,参数类型 参数名...)
{
}
static class Tools
{
// int 类型拓展一个成员方法
// value 代表使用该方法的实例化对象
// 无参扩展
public static void SpeakValue(this int value)
{
Console.WriteLine("拓展方法:" +value);
}
// string 类型拓展
// 有参拓展
public static void StringInfo(this string str, string str2, string str3)
{
Console.WriteLine(str + str2 + str3);
}
}
static void Main(string[] args)
{
int i = 10;
i.SpeakValue(); // 输出:拓展方法:10
string str = "hello";
str.StringInfo("你好","大家好"); // 输出:hello你好大家好
}
11.运算符重载
1.基本概念:
运算符重载允许为自定义类或结构体类型重新定义运算符的行为,使其支持类似内置类型的操作(如 +、-、== 等)。通过重载,可以提升代码的可读性和简洁性。
2.关键字:operator
3.特点:
1.是一个公共的静态方法
2.返回值写在operator前
3.逻辑处理自定义
可以重载的运算符:
1.算数运算符都可以重载
2.逻辑运算符只有逻辑非(!)可以重载
3.位运算符都可以重载
4.条件运算符也可以重载,但需要成对出现
不可以重载的运算符:
1.逻辑与(&&)、逻辑或(||)
2.索引符[]
3.括号强制运算符()
4.特殊运算符(点、三目运算符、赋值运算符)
注意:
1.条件运算符需要成对出现
2.一个符号可以多次重载
3.不能使用ref和out
4.基本语法:
public static 返回类型 operator 运算符(参数1, 参数2)
class Point
{
// 成员属性
public int x{get;};
public int y{get;};
// 构造函数
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
// +号的重载:向量加法
public static Point operator +(Point p1, Point p2)
{
Point p = new Point();
p.x = p1.x + p2.x;
p.y = p1.y + p2.y;
return p;
}
}
static void Main(string[] args)
{
Ponit p1 = new Point(1,2);
Point p2 = new Point(9, 3);
// var是一个隐式类型推断关键字,它允许编译器根据变量的初始化值自动推断其类型。
// var 声明时必须初始化
var p2 = p1 + p2;
}
12.内部类和分部类
1.内部类基本概念
内部类:一个类里面在定义一个类。它可以访问外部类的成员(包括私有成员)
特点:使用时(类1.类2.xxx)
public class OuterClass
{
private int outerField = 10;
private static int staticicField = 20;
// 非静态内部类
public class InnerClass
{
public void Display(OuterClass outer)
{
Console.WriteLine(outer.outerField); // 访问外部类私有成员
}
}
// 静态内部类
public static class StaticInnerClass
{
public static void Print()
{
Console.WriteLine(staticField); // ✅ 可访问外部类静态成员
}
}
}
// 使用
var outer = new OuterClass(); // 实例化外部类
var inner = new OuterClass.InnerClass(); // 实例化内部类
inner.Display(outer);
// 静态内部类
OuterClass.StaticInnerClass.Print(); // ✅ 直接通过类名调用
2.分部类基本概念
将一个类的定义分散在多个文件中,编译器在编译时会将这些部分合并为一个完整的类。
关键字:partial
注意:
1.分部类可以写在多个脚本上
2.分部类的访问修饰符要一致
3.分部类不能出现重复成员变量
// 文件1: Person.cs
public partial class Person
{
public string Name { get; set; }
public void Walk()
{
Console.WriteLine("Walking...");
}
}
// 文件2: PersonExtras.cs
public partial class Person
{
public int Age { get; set; }
public void Talk()
{
Console.WriteLine("Talking...");
}
}
3.分部方法基本概念
分部方法是 C# 中一种特殊的方法类型,它允许将方法的声明和实现分离到不同的文件中,主要用于提供可选的扩展点。
注意:
1.分部方法不能加访问修饰符,默认位private
2.只能再分部类中声明
3.返回值只能是void
4.可以有参数但不能用out关键字
// 文件1: DataProcessor.cs (声明)
public partial class DataProcessor
{
// 分部方法声明(无实现体)
partial void PreProcess();
public void Process()
{
PreProcess(); // 调用
Console.WriteLine("Main processing");
}
}
// 文件2: DataProcessorExt.cs (可选实现)
public partial class DataProcessor
{
// 分部方法实现
partial void PreProcess()
{
Console.WriteLine("Pre-processing data");
}
}
更多推荐


所有评论(0)