用C# 实现 OOP,AOP,封装、继承、多态以及方法名称后面跟着 where 关键字的用法,这其实是泛型约束(Generic Constraints) 的核心语法,用来限制泛型类型参数的范围。
本文系统介绍了面向对象编程(OOP)的三大核心特性:封装通过私有字段和公共属性保护数据安全;继承实现代码复用;多态允许子类重写父类方法。同时详细讲解了面向切面编程(AOP)的概念,它通过切面特性(如日志、性能监控)与核心业务逻辑解耦,演示了基于特性+反射的AOP实现方式,并与工业级AOP框架进行对比。文章通过交通工具的完整示例,展示了OOP三大特性与AOP的结合应用,最后还补充了泛型约束、设计模式
前言
本文系统介绍了面向对象编程(OOP)的三大核心特性:封装通过私有字段和公共属性保护数据安全;继承实现代码复用;多态允许子类重写父类方法。同时详细讲解了面向切面编程(AOP)的概念,它通过切面特性(如日志、性能监控)与核心业务逻辑解耦,演示了基于特性+反射的AOP实现方式,并与工业级AOP框架进行对比。文章通过交通工具的完整示例,展示了OOP三大特性与AOP的结合应用,最后还补充了泛型约束、设计模式等进阶内容,为开发者提供了全面的编程范式指导。

对象编程(OOP)
面向对象编程(OOP)的三大核心特性:封装、继承和多态
通过一个贴近实际的示例(以 “交通工具” 为场景)来清晰展示这三个特性的具体实现和应用。
一、OOP核心概念先理解
- 封装:将数据(字段)和操作数据的方法封装在类中,通过访问修饰符(如
private/public)控制外部访问,只暴露必要的接口。 - 继承:子类继承父类的属性和方法,实现代码复用,同时可扩展或重写父类功能。
- 多态:同一行为在不同对象上表现出不同形态,C# 中主要通过虚方法重写(virtual/override) 或接口实现 实现。
二、完整代码实现
以下代码通过 “交通工具(父类)→ 汽车 / 电动车(子类)” 的层级,完整展示三大特性:
using System;
namespace OOP_Demo
{
// ===================== 1. 封装:父类(交通工具) =====================
/// <summary>
/// 交通工具基类(封装核心属性和通用方法)
/// </summary>
public class Vehicle
{
// 私有字段(封装:外部无法直接访问)
private string _brand; // 品牌
private int _speed; // 速度
// 公共属性(封装:通过属性控制字段的读写,可添加校验逻辑)
public string Brand
{
get => _brand; // 只读逻辑
set
{
// 封装:对输入进行校验,保证数据合法性
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("品牌不能为空");
_brand = value;
}
}
public int Speed
{
get => _speed;
set
{
if (value < 0)
throw new ArgumentOutOfRangeException("速度不能为负数");
_speed = value;
}
}
// 构造函数(封装:初始化私有字段)
public Vehicle(string brand, int speed)
{
Brand = brand;
Speed = speed;
}
// 虚方法(为多态做准备:允许子类重写)
public virtual void Run()
{
Console.WriteLine($"{Brand} 以 {Speed} km/h 行驶(通用交通工具)");
}
// 封装的通用方法
public void Stop()
{
Console.WriteLine($"{Brand} 已停止行驶");
Speed = 0;
}
}
// ===================== 2. 继承 + 多态:子类(汽车) =====================
/// <summary>
/// 汽车类(继承自Vehicle,扩展并重写父类方法)
/// </summary>
public class Car : Vehicle
{
// 子类新增属性(封装)
private int _fuel; // 油量
public int Fuel
{
get => _fuel;
set
{
if (value < 0 || value > 100)
throw new ArgumentOutOfRangeException("油量需在0-100之间");
_fuel = value;
}
}
// 子类构造函数(必须调用父类构造函数)
public Car(string brand, int speed, int fuel) : base(brand, speed)
{
Fuel = fuel;
}
// 多态:重写父类的Run方法
public override void Run()
{
Console.WriteLine($"{Brand} 汽车(油量{Fuel}%)以 {Speed} km/h 行驶,耗油中...");
}
// 子类新增方法(封装)
public void Refuel(int amount)
{
Fuel += amount;
Console.WriteLine($"{Brand} 加油{amount}%,当前油量:{Fuel}%");
}
}
// ===================== 2. 继承 + 多态:子类(电动车) =====================
/// <summary>
/// 电动车类(继承自Vehicle,扩展并重写父类方法)
/// </summary>
public class ElectricCar : Vehicle
{
// 子类新增属性(封装)
private int _battery; // 电量
public int Battery
{
get => _battery;
set
{
if (value < 0 || value > 100)
throw new ArgumentOutOfRangeException("电量需在0-100之间");
_battery = value;
}
}
// 子类构造函数
public ElectricCar(string brand, int speed, int battery) : base(brand, speed)
{
Battery = battery;
}
// 多态:重写父类的Run方法
public override void Run()
{
Console.WriteLine($"{Brand} 电动车(电量{Battery}%)以 {Speed} km/h 行驶,耗电中...");
}
// 子类新增方法(封装)
public void Charge(int amount)
{
Battery += amount;
Console.WriteLine($"{Brand} 充电{amount}%,当前电量:{Battery}%");
}
}
// ===================== 测试代码 =====================
class Program
{
static void Main(string[] args)
{
try
{
// 1. 测试封装:通过属性控制数据合法性(错误示例)
// var invalidCar = new Car("宝马", 120, -10); // 会抛出油量异常
// 2. 实例化子类对象
Car bmw = new Car("宝马", 120, 80);
ElectricCar tesla = new ElectricCar("特斯拉", 150, 70);
// 3. 测试继承:调用父类的Stop方法
bmw.Run(); // 多态:执行Car重写的Run
bmw.Refuel(10); // 子类独有方法
bmw.Stop(); // 继承自父类的方法
Console.WriteLine("-----");
tesla.Run(); // 多态:执行ElectricCar重写的Run
tesla.Charge(20); // 子类独有方法
tesla.Stop(); // 继承自父类的方法
// 4. 多态的核心:父类引用指向子类对象
Vehicle vehicle1 = new Car("奔驰", 110, 60);
Vehicle vehicle2 = new ElectricCar("比亚迪", 130, 85);
Console.WriteLine("-----");
vehicle1.Run(); // 实际执行Car的Run
vehicle2.Run(); // 实际执行ElectricCar的Run
}
catch (Exception ex)
{
Console.WriteLine("错误:" + ex.Message);
}
}
}
}
三、代码关键解释
1. 封装的体现
- 所有字段(如
_brand、_fuel)都用private修饰,外部无法直接修改; - 通过
public属性(如Brand、Fuel)暴露访问,且属性的set方法中添加了数据校验(如油量不能为负),保证数据合法性; - 类中的方法(如
Refuel、Charge)封装了具体的业务逻辑,外部只需调用方法,无需关心内部实现。
2. 继承的体现
Car和ElectricCar都通过: Vehicle继承自父类Vehicle;- 子类自动拥有父类的属性(
Brand、Speed)和方法(Stop),无需重复编写; - 子类构造函数通过
base(brand, speed)调用父类构造函数,初始化父类的字段。
3. 多态的体现
- 父类
Vehicle中的Run方法用virtual标记,允许子类重写; - 子类
Car和ElectricCar用override重写Run方法,实现各自的行驶逻辑; - 核心:父类类型的变量(
Vehicle vehicle1)可以指向子类对象(new Car(...)),调用Run时会执行子类的实现,而非父类的通用实现。
四、运行结果
宝马 汽车(油量80%)以 120 km/h 行驶,耗油中...
宝马 加油10%,当前油量:90%
宝马 已停止行驶
-----
特斯拉 电动车(电量70%)以 150 km/h 行驶,耗电中...
特斯拉 充电20%,当前电量:90%
特斯拉 已停止行驶
-----
奔驰 汽车(油量60%)以 110 km/h 行驶,耗油中...
比亚迪 电动车(电量85%)以 130 km/h 行驶,耗电中...
OOP总结
- 封装:核心是 “隐藏内部细节,暴露安全接口”,通过
private字段 +public属性 / 方法实现,保证数据安全和代码可控; - 继承:核心是 “复用父类代码,扩展子类功能”,子类通过
: 父类继承,需调用父类构造函数,可新增属性 / 方法; - 多态:核心是 “同一行为,不同表现”,通过
virtual(父类)+override(子类)实现,支持父类引用指向子类对象,提高代码灵活性。
这三个特性是 OOP 的核心,封装保证代码的安全性,继承保证代码的复用性,多态保证代码的扩展性,三者结合能写出高内聚、低耦合的优质代码。
AOP(面向切面编程)
一、先理解 AOP 核心概念
AOP(Aspect-Oriented Programming,面向切面编程)是对 OOP 的补充,核心思想是:
- 将核心业务逻辑(比如汽车行驶、充电)和通用横切逻辑(比如日志记录、性能监控、异常处理)分离;
- 这些横切逻辑(如 “记录行驶日志”)就像一个 “切面”,可以动态植入到核心业务的执行流程中,无需修改核心代码。
- C# 中实现 AOP 最常用的方式有两种:特性(Attribute)+ 反射(入门级)、第三方框架(如 Autofac.DynamicProxy、PostSharp)(工业级)。
二、结合 OOP 示例实现 AOP(特性 + 反射)
下面基于之前的 “交通工具” 示例,新增 AOP 功能:给 Run() 方法添加日志记录和性能监控的切面逻辑,不修改原有核心代码。
1. 完整代码实现
csharp
运行
using System;
using System.Diagnostics;
using System.Reflection;
namespace OOP_AOP_Demo
{
// ===================== 第一步:定义AOP切面特性 =====================
/// <summary>
/// 日志记录切面特性(标记在方法上,实现日志记录)
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class LogAttribute : Attribute
{
// 切面逻辑:执行方法前后记录日志
public void ExecuteBefore(string methodName, object instance)
{
Console.WriteLine($"[日志] {DateTime.Now} - 开始执行 {instance.GetType().Name}.{methodName} 方法");
}
public void ExecuteAfter(string methodName, object instance)
{
Console.WriteLine($"[日志] {DateTime.Now} - 完成执行 {instance.GetType().Name}.{methodName} 方法");
}
}
/// <summary>
/// 性能监控切面特性(标记在方法上,统计方法执行耗时)
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class PerformanceAttribute : Attribute
{
private Stopwatch _stopwatch;
// 切面逻辑:方法执行前启动计时器
public void ExecuteBefore()
{
_stopwatch = Stopwatch.StartNew();
}
// 切面逻辑:方法执行后停止计时器,输出耗时
public void ExecuteAfter(string methodName, object instance)
{
_stopwatch.Stop();
Console.WriteLine($"[性能] {instance.GetType().Name}.{methodName} 执行耗时:{_stopwatch.ElapsedMilliseconds} ms");
}
}
// ===================== 第二步:AOP切面执行器(核心) =====================
/// <summary>
/// AOP执行器:通过反射执行标记了切面特性的方法,并植入切面逻辑
/// </summary>
public static class AopExecutor
{
public static void ExecuteMethod(object instance, string methodName)
{
// 1. 获取目标方法
MethodInfo method = instance.GetType().GetMethod(methodName);
if (method == null)
{
throw new ArgumentException($"方法 {methodName} 不存在");
}
// 2. 提取方法上的切面特性
var logAttr = method.GetCustomAttribute<LogAttribute>();
var perfAttr = method.GetCustomAttribute<PerformanceAttribute>();
// 3. 执行切面的“前置逻辑”
logAttr?.ExecuteBefore(methodName, instance);
perfAttr?.ExecuteBefore();
try
{
// 4. 执行核心业务方法(原有OOP逻辑)
method.Invoke(instance, null);
}
catch (Exception ex)
{
Console.WriteLine($"[异常] 执行 {methodName} 失败:{ex.InnerException?.Message ?? ex.Message}");
}
// 5. 执行切面的“后置逻辑”
logAttr?.ExecuteAfter(methodName, instance);
perfAttr?.ExecuteAfter(methodName, instance);
}
}
// ===================== 第三步:复用原有OOP代码,标记切面特性 =====================
/// <summary>
/// 交通工具基类(OOP封装)
/// </summary>
public class Vehicle
{
private string _brand;
private int _speed;
public string Brand
{
get => _brand;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("品牌不能为空");
_brand = value;
}
}
public int Speed
{
get => _speed;
set
{
if (value < 0)
throw new ArgumentOutOfRangeException("速度不能为负数");
_speed = value;
}
}
public Vehicle(string brand, int speed)
{
Brand = brand;
Speed = speed;
}
// 标记AOP切面特性:日志 + 性能监控
[Log, Performance]
public virtual void Run()
{
// 模拟方法执行耗时
System.Threading.Thread.Sleep(100);
Console.WriteLine($"{Brand} 以 {Speed} km/h 行驶(通用交通工具)");
}
public void Stop()
{
Console.WriteLine($"{Brand} 已停止行驶");
Speed = 0;
}
}
/// <summary>
/// 汽车子类(OOP继承+多态)
/// </summary>
public class Car : Vehicle
{
private int _fuel;
public int Fuel
{
get => _fuel;
set
{
if (value < 0 || value > 100)
throw new ArgumentOutOfRangeException("油量需在0-100之间");
_fuel = value;
}
}
public Car(string brand, int speed, int fuel) : base(brand, speed)
{
Fuel = fuel;
}
// 同样标记AOP切面特性
[Log, Performance]
public override void Run()
{
System.Threading.Thread.Sleep(150);
Console.WriteLine($"{Brand} 汽车(油量{Fuel}%)以 {Speed} km/h 行驶,耗油中...");
}
}
/// <summary>
/// 测试代码
/// </summary>
class Program
{
static void Main(string[] args)
{
try
{
Car bmw = new Car("宝马", 120, 80);
// 通过AOP执行器调用Run方法(植入切面逻辑)
AopExecutor.ExecuteMethod(bmw, "Run");
Console.WriteLine("-----");
Vehicle baseVehicle = new Vehicle("通用交通工具", 80);
AopExecutor.ExecuteMethod(baseVehicle, "Run");
}
catch (Exception ex)
{
Console.WriteLine("错误:" + ex.Message);
}
}
}
}
2. 代码关键解释
(1)AOP 核心实现(切面特性 + 执行器)
- 切面特性(Attribute):
LogAttribute和PerformanceAttribute是自定义特性,封装了 “日志记录”“性能监控” 的横切逻辑,标记在需要增强的方法上(如Run()); - AOP 执行器(AopExecutor):通过反射获取方法上的切面特性,在执行核心方法(
Run())的前 / 后自动执行切面逻辑,实现 “无侵入式” 增强。
(2)OOP + AOP 结合
原有 OOP 代码(封装、继承、多态)完全无需修改,只需给 Run() 方法标记 [Log, Performance] 特性,就能自动获得日志和性能监控能力,这正是 AOP 的核心价值 ——横切逻辑与核心业务解耦。
3. 运行结果
plaintext
[日志] 2026/1/22 15:00:00 - 开始执行 Car.Run 方法
宝马 汽车(油量80%)以 120 km/h 行驶,耗油中...
[日志] 2026/1/22 15:00:00 - 完成执行 Car.Run 方法
[性能] Car.Run 执行耗时:152 ms
-----
[日志] 2026/1/22 15:00:00 - 开始执行 Vehicle.Run 方法
通用交通工具 以 80 km/h 行驶(通用交通工具)
[日志] 2026/1/22 15:00:00 - 完成执行 Vehicle.Run 方法
[性能] Vehicle.Run 执行耗时:101 ms
三、工业级 AOP 实现(补充)
上面的 “特性 + 反射” 是入门级实现,实际项目中更常用 动态代理 框架(如 Autofac.DynamicProxy、Castle DynamicProxy),优势是:
- 无需手动写反射执行器,通过接口 / 虚方法自动生成代理类;
- 支持更复杂的切面逻辑(如异常拦截、事务管理);
- 性能比纯反射更高。
示例(Autofac.DynamicProxy 简化版):
csharp
运行
// 1. 安装NuGet包:Autofac、Autofac.DynamicProxy
// 2. 定义拦截器(切面逻辑)
public class LogInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
// 前置逻辑
Console.WriteLine($"[日志] 开始执行 {invocation.Method.Name}");
// 执行核心方法
invocation.Proceed();
// 后置逻辑
Console.WriteLine($"[日志] 完成执行 {invocation.Method.Name}");
}
}
// 3. 注册并使用代理
var builder = new ContainerBuilder();
builder.RegisterType<Car>().As<Vehicle>().EnableInterfaceInterceptors();
builder.RegisterType<LogInterceptor>();
using var container = builder.Build();
var car = container.Resolve<Vehicle>();
car.Run(); // 自动触发拦截器(AOP)
AOP vs AOP总结
- OOP vs AOP:OOP 按 “对象 / 业务” 划分代码(如汽车、电动车),AOP 按 “横切逻辑” 划分代码(如日志、性能),是对 OOP 的补充;
- C# 实现 AOP:入门级用 “自定义特性 + 反射”,工业级用动态代理框架(Autofac.DynamicProxy/PostSharp);
- 核心价值:AOP 让通用横切逻辑(日志、监控、事务)与核心业务解耦,无需重复编写,降低代码冗余。
泛型约束(Generic Constraints)
C# 中方法名称后面跟着 where 关键字的用法,这其实是泛型约束(Generic Constraints) 的核心语法,用来限制泛型类型参数的范围。
一、核心概念解释
在 C# 中,where 关键字专门用于泛型(方法、类、接口)中,作用是给泛型类型参数 T 设定规则,避免我们随意传入不符合要求的类型,同时也能让编译器识别出 T 可以调用的方法 / 属性。
简单来说:没有约束的泛型 T 只能调用 object 基类的方法(如 ToString());加了 where 约束后,T 就可以调用约束类型的所有成员。
二、常见用法示例
下面通过具体代码展示不同场景下 where 的用法:
1. 基础用法:约束泛型方法的类型参数
using System;
public class GenericMethodDemo
{
// 泛型方法:T 必须是实现了 IComparable 接口的类型(才能调用 CompareTo 方法)
public static T Max<T>(T a, T b) where T : IComparable<T>
{
// 因为有 where 约束,编译器知道 T 有 CompareTo 方法
return a.CompareTo(b) > 0 ? a : b;
}
public static void Main()
{
// 合法:int 实现了 IComparable<int>
int intMax = Max(10, 20);
Console.WriteLine($"整数最大值:{intMax}"); // 输出:20
// 合法:string 实现了 IComparable<string>
string strMax = Max("apple", "banana");
Console.WriteLine($"字符串最大值:{strMax}"); // 输出:banana
// 非法(编译报错):自定义类未实现 IComparable,不符合 where 约束
// MyClass obj1 = new MyClass();
// MyClass obj2 = new MyClass();
// Max(obj1, obj2);
}
}
// 自定义测试类(未实现 IComparable)
public class MyClass { }
2. 多种常见约束类型
where 支持多种约束,满足不同场景需求:
using System;
public class ConstraintTypesDemo
{
// 约束1:T 必须是引用类型(class)
public static void RefTypeMethod<T>(T obj) where T : class
{
// 可以安全地将 T 赋值为 null(引用类型特性)
T nullObj = null;
}
// 约束2:T 必须是值类型(struct)
public static void ValueTypeMethod<T>(T obj) where T : struct
{
// T 不能为 null(值类型特性,可空值类型除外)
}
// 约束3:T 必须有无参构造函数(new())
public static T CreateInstance<T>() where T : new()
{
// 可以直接 new T(),因为约束保证了无参构造存在
return new T();
}
// 约束4:组合约束(T 是引用类型 + 实现 IDisposable)
public static void DisposableMethod<T>(T obj) where T : class, IDisposable
{
obj.Dispose(); // 合法,因为 T 实现了 IDisposable
}
}
3. 泛型类中的 where 用法(扩展)
除了方法,类的泛型参数也可以用 where:
// 泛型类:T 必须继承自 Animal 类
public class AnimalShelter<T> where T : Animal
{
public void Adopt(T animal)
{
animal.MakeSound(); // 合法,因为 T 是 Animal 的子类
}
}
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("动物发出声音");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("汪汪汪");
}
}
三、关键约束类型汇总
| 约束语法 | 作用 |
|---|---|
where T : class |
T 必须是引用类型(如 string、自定义类) |
where T : struct |
T 必须是值类型(如 int、DateTime) |
where T : new() |
T 必须有公共无参构造函数 |
where T : 基类名 |
T 必须是该基类的子类 / 本身 |
where T : 接口名 |
T 必须实现该接口 |
where T : U |
T 必须是 U 的子类 / 本身(泛型参数约束) |
泛型约束总结
where是 C# 泛型的核心约束关键字,用于限制泛型类型参数的范围,避免类型滥用;- 加了
where约束后,编译器能识别泛型 T 的成员,允许调用约束类型的方法 / 属性; - 常见约束包括
class/struct/new()/ 基类 / 接口,可组合使用(注意new()必须放在最后)。
通过 where 约束,你可以写出更安全、更灵活的泛型代码,既保留泛型的通用性,又避免无意义的类型传入。
C# 中如何给泛型接口使用 where 关键字来约束泛型类型参数
在 C# 中如何给泛型接口使用 where 关键字来约束泛型类型参数,这和泛型方法 / 类的 where 用法核心逻辑一致,只是作用对象变成了接口的泛型参数。
一、核心用法:泛型接口 + where 约束
泛型接口的 where 约束写在接口名称和泛型参数之后,语法和泛型类 / 方法完全兼容,目的是限制实现该接口的类必须使用符合约束的类型参数。
1. 基础示例:接口约束泛型参数实现指定接口
using System;
// 泛型接口:约束 T 必须实现 IComparable<T> 接口
public interface ISorter<T> where T : IComparable<T>
{
// 因为有 where 约束,方法内可以安全调用 T 的 CompareTo 方法
T[] Sort(T[] array);
}
// 实现接口:int 符合约束(实现了 IComparable<int>),所以合法
public class IntSorter : ISorter<int>
{
public int[] Sort(int[] array)
{
Array.Sort(array);
return array;
}
}
// 测试代码
public class Test
{
public static void Main()
{
var sorter = new IntSorter();
int[] nums = { 3, 1, 2 };
int[] sortedNums = sorter.Sort(nums);
Console.WriteLine(string.Join(", ", sortedNums)); // 输出:1, 2, 3
}
}
2. 多约束示例:接口约束泛型参数为引用类型 + 实现接口
// 泛型接口:T 必须是引用类型 + 实现 IDisposable 接口
public interface IResourceManager<T> where T : class, IDisposable
{
// 管理资源:创建 → 使用 → 释放
void ManageResource(T resource);
}
// 实现接口:FileStream 是引用类型 + 实现 IDisposable,符合约束
public class FileManager : IResourceManager<FileStream>
{
public void ManageResource(FileStream resource)
{
// 使用资源
byte[] buffer = new byte[1024];
resource.Read(buffer, 0, buffer.Length);
// 释放资源(因为 T 实现了 IDisposable)
resource.Dispose();
}
}
3. 接口约束泛型参数为值类型 / 有参构造
// 泛型接口:T 必须是值类型
public interface ICalculator<T> where T : struct
{
T Add(T a, T b);
}
// 实现接口:double 是值类型,符合约束
public class DoubleCalculator : ICalculator<double>
{
public double Add(double a, double b)
{
return a + b;
}
}
// 泛型接口:T 必须有无参构造函数
public interface IFactory<T> where T : new()
{
// 创建 T 的实例(因为 new() 约束,可直接 new T())
T CreateInstance();
}
// 实现接口:Person 有无参构造,符合约束
public class PersonFactory : IFactory<Person>
{
public Person CreateInstance()
{
return new Person();
}
}
public class Person
{
// 无参构造函数(满足 new() 约束)
public Person() { }
public string Name { get; set; }
}
4. 接口继承中的 where 约束(进阶)
当泛型接口继承另一个泛型接口时,子接口可以继承 / 强化父接口的约束:
csharp
运行
// 父接口:约束 T 是引用类型
public interface IBaseInterface<T> where T : class
{
void DoSomething(T obj);
}
// 子接口:继承父接口约束 + 新增 T 实现 ICloneable 的约束
public interface IChildInterface<T> : IBaseInterface<T> where T : class, ICloneable
{
T Clone(T obj);
}
// 实现子接口:string 是引用类型 + 实现 ICloneable,符合约束
public class StringHandler : IChildInterface<string>
{
public void DoSomething(string obj)
{
Console.WriteLine($"处理字符串:{obj}");
}
public string Clone(string obj)
{
return (string)obj.Clone(); // 合法,因为 T 实现了 ICloneable
}
}
二、注意事项
- 约束的继承性:实现泛型接口的类,其指定的类型参数必须完全满足接口的
where约束,否则编译报错; - new () 约束位置:如果接口的
where包含new(),必须放在所有约束的最后(如where T : class, IDisposable, new()); - 接口本身非泛型时:普通非泛型接口不能用
where,where仅针对泛型接口的类型参数。
泛型接口总结
- 泛型接口的
where语法和泛型类 / 方法一致,写在接口名称 + 泛型参数后,用于约束接口的类型参数范围; - 实现泛型接口的类,其指定的类型必须满足接口的
where约束,否则无法编译; - 接口的
where支持组合约束(如class + 接口、struct + new()),核心目的是保证接口内的代码能安全调用泛型参数的成员。
通过给泛型接口加 where 约束,你可以确保所有实现该接口的类都遵循统一的类型规则,避免传入不符合预期的类型,让接口的通用性和安全性兼得。
其他
七大原则,24种设计模式,aop多种实现方式
更多推荐



所有评论(0)