C#接口的定义与使用

发布于:2025-08-04 ⋅ 阅读:(14) ⋅ 点赞:(0)

第1章 接口(interface)是什么

1.1 定义

• 接口是一组“能力”或“契约”的抽象描述,只规定“能做什么”,不规定“怎么做”。

• 在 C# 中,接口是一种完全抽象的类型(fully abstract type)。
• 关键词:interface。
• 命名规范:以大写字母 I 开头,如 ILogger、IDisposable。

1.2 语法骨架
public interface 接口名
{
    // 1. 属性
    数据类型 属性名 { get; set; }   // 自动属性写法
    // 2. 方法
    返回类型 方法名(参数列表);
    // 3. 索引器
    数据类型 this[int index] { get; set; }
    // 4. 事件
    event EventHandler 事件名;
}

• 成员默认 public,不允许显式写 public/private/protected。

• 不允许有字段(field) 和 实例构造函数。

第2章 实现接口的三种姿势

2.1 隐式实现(Implicit Implementation)
public class FileLogger : ILogger
{
    public void Log(string message) => Console.WriteLine($"[File] {message}");
}

• 类中 public 成员签名与接口成员一致即可。

• 通过类变量或接口变量都能调用。

2.2 显式实现(Explicit Implementation)
public class DbLogger : ILogger
{
    void ILogger.Log(string message)   // 无访问修饰符
        => Console.WriteLine($"[DB] {message}");
}

• 只能“接口变量”调用:ILogger log = new DbLogger(); log.Log(...)

• 解决“菱形继承”时成员命名冲突。

2.3 结构体实现接口

public struct Point : IEquatable<Point>
{
    public int X, Y;
    public bool Equals(Point other) => X == other.X && Y == other.Y;
}

• 值类型也可实现接口,但注意装箱问题。

第3章 接口的进阶特性

interface IRunnable { void Run(); }
interface ILogger   { void Log(string msg); }

class Job : IRunnable, ILogger
{
    public void Run() => Log("Job is running");
    public void Log(string msg) => Console.WriteLine(msg);
}
3.2 接口继承接口
interface IAdvancedLogger : ILogger
{
    void LogError(Exception ex);
}

实现类必须实现全部成员(包括继承链)。

3.3 默认实现(C# 8.0+)
public interface ILogger
{
    void Log(string message);
    void LogError(Exception ex) => Log($"Error: {ex.Message}");
}

• 实现类不强制实现 LogError。

• 解决“接口演化”问题,向后兼容。

3.4 静态抽象成员(C# 11+)
public interface IFactory<T> where T : new()
{
    static abstract T Create();
}

用于泛型数学、泛型工厂等高级场景。

第4章 完整案例:插件式日志系统

4.1 需求

• 系统同时支持控制台、文件、数据库三种日志输出。

• 可随时插拔新的日志方式。

• 调用端面向接口编程,不依赖具体实现。

4.2 设计
  1. 定义接口 ILogger

  2. 三种实现:ConsoleLogger、FileLogger、SqlLogger

  3. 使用“工厂 + 反射”做插件加载

  4. 客户端仅依赖 ILogger

4.3 代码实现 

(1) 接口:

public interface ILogger
{
    void Log(string message);
    void LogError(Exception ex);
}

(2) 控制台实现:

public class ConsoleLogger : ILogger
{
    public void Log(string message) => Console.WriteLine($"[Console] {DateTime.Now}: {message}");
    public void LogError(Exception ex) => Log($"ERROR: {ex}");
}

(3) 文件实现:

public class FileLogger : ILogger
{
    private readonly string _path;
    public FileLogger(string path) => _path = path;

    public void Log(string message)
        => File.AppendAllText(_path, $"{DateTime.Now}: {message}\n");

    public void LogError(Exception ex) => Log($"ERROR: {ex}");
}

(4) 工厂(简化版):

public static class LoggerFactory
{
    public static ILogger Create(string type, params object[] args)
    {
        var t = Type.GetType(type, true);
        return (ILogger)Activator.CreateInstance(t, args);
    }
}

(5) 配置文件 appsettings.json

{
  "Logger": {
    "Type": "FileLogger",
    "Args": [ "app.log" ]
  }
}

(6) LoggerConfig 的定义

// 通常放在 Models 文件夹或 Config 文件夹下
public class LoggerConfig
{
    // 对应 JSON 里的 "Type"
    public string Type { get; set; }

    // 对应 JSON 里的 "Args",由于 JSON 里是数组,所以用 object[]
    public object[] Args { get; set; } = Array.Empty<object>();
}

(7) 客户端:

var config = JsonSerializer.Deserialize<LoggerConfig>(File.ReadAllText("appsettings.json"));
ILogger logger = LoggerFactory.Create(config.Type, config.Args);

logger.Log("系统启动");
try { int x = 0, y = 1 / x; }
catch (Exception ex) { logger.LogError(ex); }

• 更换日志方式只需改配置,无需改业务代码。

第5章 常见误区与最佳实践

  1. 接口 vs 抽象类
    • 接口:多继承、纯契约、无状态。
    • 抽象类:单继承、可包含状态、可含实现。

  2. 接口隔离原则(ISP)
    • 不要强迫客户依赖它们不用的方法。
    • 把“胖接口”拆分为多个小接口。

  3. 命名规范
    • 接口以 I 开头,实现类以接口名去掉 I 为后缀,如 SqlLogger : ILogger。

  4. 显式实现的代价
    • 调用方必须转换成接口类型,降低可读性;除非解决冲突,否则优先隐式实现。


网站公告

今日签到

点亮在社区的每一天
去签到