C#基础09-面向对象关键字
零、文章目录
C#基础09-面向对象关键字
1、new 关键字
(1)作为运算符(创建对象)
- 作用:在托管堆上分配内存并调用构造函数,返回对象引用。
- 语法:
ClassName obj = new ClassName(); // 引用类型
int num = new int(); // 值类型(等价于 int num = 0)
- 场景:
- 实例化类、结构体或数组(如
List<int> list = new List<int>();
)。 - 为值类型调用默认构造函数(初始化默认值,如
int
初始化为 0)。
- 实例化类、结构体或数组(如
(2)作为修饰符(隐藏继承成员)
- 作用:显式隐藏基类中的同名成员(字段、方法、属性等),消除编译器警告。
- 语法:
public class BaseClass {public void Method() => Console.WriteLine("Base");
}public class DerivedClass : BaseClass {new public void Method() => Console.WriteLine("Derived"); // 隐藏基类方法
}
- 关键点:
- 隐藏后,通过派生类实例访问的是新成员;通过基类引用访问原成员。
- 与
override
区别:new
创建新成员(无多态),override
扩展基类成员(支持多态)。
DerivedClass d = new DerivedClass();
d.Method(); // 输出 "Derived"
((BaseClass)d).Method(); // 输出 "Base"
(3)作为泛型约束(限制类型参数)
- 作用:要求泛型类型参数必须有公共无参构造函数。
- 语法:
public class Factory<T> where T : new() {public T Create() => new T(); // 可安全实例化 T
}
- 场景:泛型工厂模式(如
Factory<Employee>.Create()
)。
(4)注意事项
- 命名冲突解决:优先使用
new
修饰符而非忽略警告,明确设计意图。 - 内存管理:
new
运算符为引用类型分配堆内存,需注意垃圾回收机制。 - 值类型优化:
new int()
与int num = 0
等效,编译器自动优化。
2、this 关键字
(1)四大核心作用
- 区分成员变量与局部变量:当方法参数与类字段同名时,使用
this
明确指定成员变量:
public class Person {private string name;public void SetName(string name) {this.name = name; // this.name 指代成员变量,name 为参数}
}
- 链式调用构造函数:在构造函数中使用
this
调用同类其他构造函数,减少代码重复:
public class Student {public string Name;public int Age;public Student(string name) : this(name, 18) { } // 调用下方构造函数public Student(string name, int age) {Name = name;Age = age;}
}
- 传递当前对象引用:将当前实例作为参数传递给其他方法或类:
public class Logger {public void Log(object obj) { /* 记录对象状态 */ }
}
public class User {public void Save() {new Logger().Log(this); // 传递当前 User 实例 }
}
- 支持扩展方法:为现有类型添加新方法时,
this
修饰首参数以绑定目标类型,无需修改源码即可增强类型功能
public static class StringExtensions {public static bool IsNumeric(this string str) { // this 绑定 string 类型return double.TryParse(str, out _);}
}
// 调用:"123".IsNumeric();
(2)关键限制与注意事项
场景 | 是否允许使用 this | 说明 |
---|---|---|
静态方法(static ) | ❌ 禁止 | 静态方法无实例上下文,使用将导致编译错误 |
静态属性访问器 | ❌ 禁止 | 同静态方法限制 |
结构体(struct ) | ⚠️ 受限 | this 在结构体中是只读变量(不可修改引用) |
构造函数链式调用 | ✅ 推荐 | 仅限构造函数首行使用(如 : this(...) ) |
(3)最佳实践与性能建议
- 避免冗余使用:成员访问若无命名冲突,可省略
this
(如name = "Alice"
替代this.name = "Alice"
),提升代码简洁性。 - 慎用于高频调用场景:
this
本身无性能开销,但过度链式构造函数可能增加初始化复杂度。 - 索引器实现 :通过
this[...]
语法定义类索引器,使对象支持类似数组的访问方式,增强数据封装灵活性。
public class Collection {private int[] data = new int[10];public int this[int index] {get => data[index];set => data[index] = value;}
}
// 调用:Collection col = new(); col[0] = 100;
(4)错误用法示例
public class Example {private int id;public static void Print() {Console.WriteLine(this.id); // 错误:静态方法中禁止使用 this }
}
- ❌ 编译错误:
CS0120: 非静态字段、方法或属性“Example.id”要求对象引用
。
3、base关键字
(1)核心用途
- 调用基类构造函数
- 当基类没有无参构造函数时,必须在派生类构造函数中通过
base(参数)
显式调用基类构造函数 - 若未显式调用,编译器会尝试调用基类无参构造;若无参构造不存在,则编译报错
- 当基类没有无参构造函数时,必须在派生类构造函数中通过
public class Animal {public Animal(string name) => Console.WriteLine($"Animal: {name}");
}
public class Dog : Animal {public Dog(string name) : base(name) { // 显式调用基类构造函数Console.WriteLine("Dog created");}
}
- 调用基类被重写的方法
- 子类重写基类虚方法(
override
)后,可通过base.方法名()
保留基类逻辑 - 适用于扩展而非完全替换基类方法的场景
- 子类重写基类虚方法(
public class Base {public virtual void Show() => Console.Write("Hello");
}
public class Derived : Base {public override void Show() {base.Show(); // 调用基类方法Console.Write(" World"); // 扩展新逻辑}
}
// 输出:"Hello World"
- 访问基类被隐藏的成员
- 当子类使用
new
关键字隐藏基类同名成员时,可通过base
访问基类原始成员
- 当子类使用
public class Parent { public int Value = 100; }
public class Child : Parent {new public int Value = 200; // 隐藏基类成员public void PrintBase() => Console.WriteLine(base.Value); // 输出100
}
(2)关键限制与注意事项
- 适用场景限制:
- 仅在实例构造函数、实例方法或属性访问器中有效,静态方法中不可使用。
- 只能访问直接基类的成员,无法跨级访问更高层基类。
- 构造函数调用规则:
- 若基类存在无参构造函数,子类可省略
: base()
(编译器自动调用); - 若基类只有有参构造,子类必须显式调用
base
并传参。
- 若基类存在无参构造函数,子类可省略
- 与
this
的区别:this
指向当前类实例,base
指向直接基类实例。两者可结合使用(如this.Name
vsbase.Name
)。
(3)典型场景速查表
场景 | 代码示例 | 作用 |
---|---|---|
初始化基类字段 | public Child(int x) : base(x) { } | 确保基类数据正确初始化 |
扩展基类方法逻辑 | base.Method(); /*新增逻辑*/ | 复用基类方法并增强功能 |
解决父子类成员名冲突 | int val = base.Field; | 访问被隐藏的基类成员 |
- 注意:滥用
base
可能破坏封装性。优先通过重写设计扩展逻辑,而非频繁绕过继承层级。
4、static关键字
(1)核心作用
- 全局共享性
- 修饰的成员(变量/方法)不属于对象实例,而是属于类本身
- 所有实例共享同一份内存空间,修改一处即全局生效
class Counter {public static int Count = 0; // 所有实例共享 public Counter() { Count++; }
}
// 测试
var c1 = new Counter();
var c2 = new Counter();
Console.WriteLine(Counter.Count); // 输出 2
- 直接访问性:无需实例化类,通过
类名.成员
直接访问
Math.Pow(2, 3); // 经典静态方法调用
(2)五大使用场景
- 静态成员变量
- 特点:
- 类加载时初始化,生命周期 = 应用程序域生存期
- 默认值:数值型为
0
,引用类型为null
- 典型用途:
- 全局配置项(如日志开关)
- 跨实例共享数据(如计数器)
- 特点:
- 静态方法
- 规则:
- 只能访问静态成员,不可调用实例方法/属性
- 不能使用
this
或base
关键字
- 典型用途:
- 工具类方法(如
File.ReadAllText()
) - 工厂模式创建对象
- 工具类方法(如
- 规则:
public static Logger CreateLogger() { return new FileLogger();
}
- 静态类
- 强制约束:
- 类本身必须标记
static
,且仅包含静态成员 - 不可被实例化或继承(本质是
sealed abstract
类)
- 类本身必须标记
- 典型用途:
- 工具类(如
System.Math
) - 扩展方法容器(需配合
this
参数)
- 工具类(如
- 强制约束:
- 静态构造函数
- 特性:
- 在类首次被访问时自动执行,且仅执行一次
- 无参数和访问修饰符
- 典型用途:初始化静态成员或加载资源
- 特性:
class ConfigLoader {public static string ApiKey;static ConfigLoader() {ApiKey = File.ReadAllText("key.txt"); // 初始化}
}
- 静态局部变量 (C# 8.0+)
- 作用域:方法内定义,但生命周期延长至应用程序结束
- 典型用途:跨方法调用保持状态(替代全局变量)
void TrackCall() {static int callCount = 0; callCount++;Console.WriteLine($"Called: {callCount} times");
}
(3)关键注意事项
- 线程安全问题:静态成员默认非线程安全,高并发时需加锁
private static readonly object _lock = new object();
public static void UpdateResource() {lock(_lock) { // 修改共享资源 }
}
- 内存泄漏风险:静态引用对象不会被 GC 回收,需手动解除引用
static List<byte[]> _cache = new List<byte[]>();
// 长期持有大对象 → 可能泄漏
- 初始化顺序:静态成员初始化顺序不可控,避免循环依赖
- 测试困难:静态成员难以 Mock,过度使用降低代码可测试性
(4)最佳实践建议
- 适用场景
- ✅ 无状态工具方法(如数学计算)
- ✅ 全局配置项(如应用设置)
- ✅ 共享只读资源(如本地化字典)
- 规避场景
- ❌ 频繁修改的共享状态(优先用依赖注入)
- ❌ 替代单例模式(需控制初始化时机的场景)
- 性能优化
- 只读静态数据标记
readonly
或const
- 延迟初始化用
Lazy<T>
减少启动开销
- 只读静态数据标记
private static readonly Lazy<Logger> _logger = new Lazy<Logger>(() => new Logger());
public static Logger Instance => _logger.Value;
(5)内存模型
5、sealed关键字
(1)核心功能与使用场景
- 密封类(Sealed Class)
- 作用:禁止其他类继承(相当于 Java 的
final
)。 - 典型场景:
- 工具类(如 System.String),防止继承破坏设计逻辑。
- 安全性要求高的类(如加密模块),避免恶意重写核心方法。
- 语法:
- 作用:禁止其他类继承(相当于 Java 的
sealed class MyClass { /* 类成员 */ }
- 密封方法(Sealed Method)
- 作用:阻止子类重写父类的虚方法。
- 强制要求:必须与override联用,不能修饰非虚方法。
- 语法:
class Base { public virtual void Method() { }
}
class Derived : Base { public sealed override void Method() { } // 禁止孙类重写
}
(2)关键技术特性
- 性能优化
- 密封类无派生类,JIT 编译器可将虚方法调用转为非虚调用,提升执行效率。
- 示例:高频调用的密封类方法(如数学计算库)可减少运行时开销。
- 设计约束
- ❌ 禁止与
abstract
联用:抽象类要求被继承,而密封类禁止继承。 - ❌ 不可修饰静态方法或属性:静态成员默认不可重写,无需密封。
- ❌ 禁止与
- 替代方案
- 若需子类重写父类方法但限制孙类重写,可在中间层方法添加
sealed
。
- 若需子类重写父类方法但限制孙类重写,可在中间层方法添加
(3)最佳实践与注意事项
- 使用原则:
- 慎用密封类:过度使用会降低代码扩展性,仅用于明确无需继承的场景。
- 文档化理由:在代码注释中说明密封原因(如安全或性能需求)。
- 常见误区:
- 误用作“优化手段”:除非性能测试证明瓶颈在虚方法调用,否则优先考虑代码可维护性。
- 混淆
new
与sealed
:sealed
:禁止重写(要求方法原本是虚方法)。new
:隐藏父类同名方法(非重写)。
(4)综合示例
// 密封类示例
sealed class EncryptionService { public void Encrypt(string data) { /* 核心加密逻辑 */ }
} // 密封方法示例
class Vehicle { public virtual void StartEngine() { }
}
class Car : Vehicle { public sealed override void StartEngine() { // 固定启动流程 }
}
class SportsCar : Car { // 编译错误:无法重写密封方法 StartEngine()
}
- 设计提示:在框架开发中,密封核心类(如
System.IO.File
)可确保行为一致性。