一、接口与抽象类概述
在C#面向对象编程中,接口(Interface)和抽象类(Abstract Class)都是实现抽象的重要机制,它们为代码提供了高级别的抽象能力,但各有特点和适用场景。
1. 基本概念对比
特性 | 接口 | 抽象类 |
---|---|---|
关键字 | interface | abstract class |
实现方式 | 完全抽象,不包含实现 | 可包含部分实现 |
成员类型 | 方法、属性、事件、索引器 | 完整类成员(字段、方法等) |
继承 | 支持多实现 | 仅支持单继承 |
构造函数 | 不能有 | 可以有 |
访问修饰符 | 默认为public,不能使用其他 | 可以使用各种访问修饰符 |
二、接口(Interface)详解
1. 接口定义与实现
// 定义接口
public interface ILogger
{
void Log(string message); // 接口成员默认public
string GetLogs();
// C#8.0起支持默认实现
void Error(string message)
{
Log($"ERROR: {message}");
}
}
// 实现接口
public class FileLogger : ILogger
{
private readonly string _filePath;
public FileLogger(string filePath)
{
_filePath = filePath;
}
public void Log(string message)
{
File.AppendAllText(_filePath, $"{DateTime.Now}: {message}\n");
}
public string GetLogs()
{
return File.Exists(_filePath) ? File.ReadAllText(_filePath) : string.Empty;
}
}
2. 接口特性
- 多实现:一个类可以实现多个接口
- 接口继承:接口可以继承其他接口
- 显式实现:解决命名冲突
public interface IDrawable
{
void Draw();
}
public interface IResizable
{
void Resize();
}
// 多接口实现
public class Circle : IDrawable, IResizable
{
// 显式接口实现
void IDrawable.Draw()
{
Console.WriteLine("绘制圆形");
}
void IResizable.Resize()
{
Console.WriteLine("调整圆形大小");
}
}
三、抽象类(Abstract Class)详解
1. 抽象类定义与继承
// 定义抽象类
public abstract class Shape
{
// 抽象方法(无实现)
public abstract double CalculateArea();
// 虚方法(有默认实现,可重写)
public virtual void Draw()
{
Console.WriteLine("绘制基本形状");
}
// 普通方法
public void PrintInfo()
{
Console.WriteLine($"面积:{CalculateArea()}");
}
}
// 继承抽象类
public class Circle : Shape
{
public double Radius { get; set; }
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
public override void Draw()
{
Console.WriteLine($"绘制圆形,半径:{Radius}");
}
}
2. 抽象类特性
- 部分实现:可以包含具体方法和抽象方法
- 构造方法:可以有构造函数
- 字段和属性:可以包含字段和完整属性
- 单继承限制:C#不支持多继承
四、接口与抽象类的选择
1. 使用接口的场景
- 需要多重继承时
- 定义行为契约而不关心实现
- 为不相关的类提供通用功能
- 需要轻量级的约定时
2. 使用抽象类的场景
- 多个相关类共享通用代码
- 需要控制子类的创建方式(通过构造函数)
- 需要提供基础实现,同时允许部分定制
- 预期未来会有更多共同功能添加
五、高级应用
1. 接口的默认实现(C#8.0+)
public interface IOrder
{
decimal Amount { get; }
// 默认实现
decimal CalculateTax()
{
return Amount * 0.1m; // 10%税
}
}
public class OnlineOrder : IOrder
{
public decimal Amount { get; set; }
// 可以选择不实现CalculateTax,使用默认实现
}
public class InternationalOrder : IOrder
{
public decimal Amount { get; set; }
// 覆盖默认实现
public decimal CalculateTax()
{
return Amount * 0.05m; // 5%税
}
}
2. 抽象类与接口的组合使用
public interface IPlayable
{
void Play();
void Pause();
}
public abstract class MediaItem : IPlayable
{
public string Title { get; set; }
public TimeSpan Duration { get; set; }
public abstract void Play();
public virtual void Pause()
{
Console.WriteLine("媒体已暂停");
}
// 抽象类自己的方法
public void DisplayInfo()
{
Console.WriteLine($"{Title} (时长:{Duration})");
}
}
public class Audio : MediaItem
{
public string Artist { get; set; }
public override void Play()
{
Console.WriteLine($"正在播放音频:{Title} - {Artist}");
}
}
public class Video : MediaItem
{
public string Director { get; set; }
public override void Play()
{
Console.WriteLine($"正在播放视频:{Title} (导演:{Director})");
}
public override void Pause()
{
Console.WriteLine("视频暂停,显示暂停画面");
}
}
六、综合示例:支付系统设计
using System;
class Program
{
static void Main()
{
PaymentProcessor processor = new PaymentProcessor();
// 使用不同支付方式
processor.ProcessPayment(new CreditCardPayment("1234-5678-9012-3456"), 100m);
processor.ProcessPayment(new PayPalPayment("user@example.com"), 150m);
processor.ProcessPayment(new BankTransferPayment("IBAN123456789"), 200m);
}
}
// 支付接口
public interface IPaymentMethod
{
string AccountInfo { get; }
bool Authorize(decimal amount);
void Process(decimal amount);
void Confirm();
}
// 抽象支付基类
public abstract class PaymentBase : IPaymentMethod
{
public string AccountInfo { get; protected set; }
public abstract bool Authorize(decimal amount);
public virtual void Process(decimal amount)
{
Console.WriteLine($"处理支付:{amount}元");
}
public virtual void Confirm()
{
Console.WriteLine("支付已确认");
}
}
// 信用卡支付
public class CreditCardPayment : PaymentBase
{
public CreditCardPayment(string cardNumber)
{
AccountInfo = $"信用卡尾号:{cardNumber.Substring(cardNumber.Length - 4)}";
}
public override bool Authorize(decimal amount)
{
Console.WriteLine($"授权信用卡支付:{amount}元");
return true; // 模拟授权成功
}
public override void Process(decimal amount)
{
base.Process(amount);
Console.WriteLine("通过信用卡网关处理支付");
}
}
// PayPal支付
public class PayPalPayment : PaymentBase
{
public PayPalPayment(string email)
{
AccountInfo = $"PayPal账户:{email}";
}
public override bool Authorize(decimal amount)
{
Console.WriteLine($"验证PayPal账户并授权:{amount}元");
return true;
}
public override void Process(decimal amount)
{
Console.WriteLine($"通过PayPal API处理支付:{amount}元");
}
}
// 银行转账
public class BankTransferPayment : PaymentBase
{
public BankTransferPayment(string iban)
{
AccountInfo = $"银行账户:{iban}";
}
public override bool Authorize(decimal amount)
{
Console.WriteLine("银行转账无需预授权");
return true;
}
public override void Confirm()
{
Console.WriteLine("银行转账已发起,等待银行处理");
}
}
// 支付处理器
public class PaymentProcessor
{
public void ProcessPayment(IPaymentMethod paymentMethod, decimal amount)
{
Console.WriteLine($"\n使用 {paymentMethod.AccountInfo} 进行支付");
if (paymentMethod.Authorize(amount))
{
paymentMethod.Process(amount);
paymentMethod.Confirm();
}
else
{
Console.WriteLine("支付授权失败");
}
}
}
七、最佳实践与设计建议
- 接口设计原则:
- 保持接口小巧专注(ISP接口隔离原则)
- 以”能力”而非”类型”命名接口(如
ISortable
而非ISorter
) - 避免”胖接口”,应该拆分为多个专门接口
- 抽象类设计建议:
- 为相关类提供有用的基础实现
- 将可能变化的成员设为virtual或abstract
- 避免过深的继承层次
- 组合使用策略:
- 优先使用接口定义契约
- 使用抽象类提供通用实现
- 考虑使用”接口+抽象类”的组合模式
- 版本控制考虑:
- 接口一旦发布难以修改(C#8.0的默认实现缓解了此问题)
- 抽象类可以添加非抽象方法而不破坏现有代码
- 性能考量:
- 接口调用有轻微性能开销(虚拟表查找)
- 抽象类方法调用通常更直接
- 在性能关键路径上应考虑此差异
接口与抽象类是C#中实现抽象的两种强大工具,理解它们的异同点和适用场景,能够帮助开发者设计出更加灵活、可维护的面向对象系统。在实际开发中,经常需要根据具体需求灵活组合使用这两种机制。