CodeGym /课程 /C# SELF /实现多个接口

实现多个接口

C# SELF
第 23 级 , 课程 2
可用

1. 为什么要实现多个接口?

当你设计一个真实系统时,对象经常不只扮演一个“角色”——而是好几个。想象一下:你有一本电子书,不仅能看,还能编辑,甚至还能同步到云端。用面向对象编程(OOP)的话来说,这个对象就得实现好几个接口:

  • IReadable —— 读内容。
  • IWritable —— 编辑内容。
  • ISyncable —— 和远程存储同步。

这时候,多接口实现就派上用场了。这是C#(其实整个OOP)的一大超能力,类没有的:你不能继承多个类,但接口你想实现多少都行。

现实生活的类比

想象一下公司里的员工。小明可以同时是:

  • 程序员(写代码)
  • 测试员(有时候帮别人测代码)
  • 经理(规划冲刺或者一天喝多少咖啡)

这些“角色”职责完全不一样。但小明都能搞定!编程里也是这样:类实现接口,承担它们的“职责”。

2. 多接口实现的语法

很简单:声明类的时候用逗号分隔接口就行:


public interface IReadable
{
    void Read();
}

public interface IWritable
{
    void Write(string text);
}

public class Note : IReadable, IWritable
{
    private string content = "";

    public void Read()
    {
        Console.WriteLine("便签: " + content);
    }

    public void Write(string text)
    {
        content = text;
        Console.WriteLine("便签已更新!");
    }
}

在这个例子里,Note类实现了两个接口。所以它必须有两个方法的实现:ReadWrite

3. “我们”应用里的例子

在之前的例子里,我们做了个简单的银行应用来管理账户。现在假设:我们需要一个通用的文档类,可以打印(IPrintable)、保存到文件(ISavable),还可能发邮件(IEmailable)。

先定义接口:


public interface IPrintable
{
    void Print();
}

public interface ISavable
{
    void Save(string filePath);
}

public interface IEmailable
{
    void Email(string toAddress);
}

一个全能的类:


public class Statement : IPrintable, ISavable, IEmailable
{
    public string Content { get; set; }

    public void Print()
    {
        Console.WriteLine("正在打印账单...");
        Console.WriteLine(Content);
    }

    public void Save(string filePath)
    {
        // 用标准库里的File.WriteAllText。
        File.WriteAllText(filePath, Content);
        Console.WriteLine($"账单已保存到文件: {filePath}");
    }

    public void Email(string toAddress)
    {
        Console.WriteLine($"账单已发送到邮箱: {toAddress}(模拟)");
    }
}

现在这个类你怎么用都行:


var stat = new Statement { Content = "本月操作: +1000 单位, -500 单位." };
stat.Print();
stat.Save("statement.txt");
stat.Email("boss@bank.corp");

顺便说一句,实际开发中,把这种对象传给只需要某个接口的方法特别方便,根本不用管它还有啥功能。比如打印方法只要参数是IPrintable,根本不用知道里面是不是“保存器”还是“发邮件的”。

4. 用接口引用:我的对象到底能干啥?

这就有点神奇了。当你实现了多个接口,你可以把对象当成它的某一个“合同”来用。比如:


IPrintable printable = new Statement { Content = "抽象讲座" };
printable.Print(); // 只能打印

// printable.Save("file.txt"); // 报错:IPrintable接口根本不知道Save。

但如果切换到另一个接口:


ISavable savable = printable as ISavable;
if (savable != null)
{
    savable.Save("file.txt");
}

这种方式很适合把对象传给只需要某个接口的方法。这样耦合度低,代码超灵活:你可以加新实现,老代码都不用动。

5. 多接口实现和不同接口里有同名方法怎么办

这就有意思了!如果两个接口都要求有同名但意义不同的方法咋办?比如,想象一下咖啡机的接口:


public interface IStartable
{
    void Start();
}

public interface IRunnable
{
    void Start();
}

咖啡机既可以“可启动”(IStartable——开始做咖啡),也可以“可运行”(IRunnable——进入工作状态)。

默认实现(普通写法):


public class CoffeeMachine : IStartable, IRunnable
{
    public void Start()
    {
        Console.WriteLine("咖啡机以两种角色启动!");
    }
}

这种情况下,一个Start()实现“覆盖”了两个接口。

如果想要不同的意义呢?

可以用显式接口实现


public class CoffeeMachine : IStartable, IRunnable
{
    void IStartable.Start()
    {
        Console.WriteLine("启动:开始制作饮品!");
    }

    void IRunnable.Start()
    {
        Console.WriteLine("启动:机器进入工作模式。");
    }
}

这种情况下,只有通过接口变量才能调用具体实现:


CoffeeMachine cm = new CoffeeMachine();

IStartable startable = cm;
startable.Start(); // 启动:开始制作饮品!

IRunnable runnable = cm;
runnable.Start(); // 启动:机器进入工作模式。

// cm.Start(); // 编译不过!Start不能直接通过类调用。

顺便说一句,.NET标准库里经常用这种写法——比如一个类实现了好几个不同框架的类似接口,每个都要不一样的行为。

6. 多接口实现和继承的区别

能力 类(继承) 接口
基类型数量 只能一个 随便几个
继承代码 可以(有基础实现) 不行,只能签名(除非有默认方法)
保存状态 可以 不行
加新角色 不行(或者很麻烦,要用组合) 很简单,实现接口就行
更适合… “物理”层级 “角色”/逻辑能力

7. 实战:混合型对象

多接口实现让你可以做出有独特“角色”组合的类,根本不用管继承那些事。

比如,在我们的银行应用里——你可以做一个类,既能保存自己信息,又能自检有效性,还能打印——全靠不同接口:


public interface IValidatable
{
    bool Validate();
}

public class Check : IPrintable, ISavable, IValidatable
{
    public string Data { get; set; }

    public void Print()
    {
        Console.WriteLine("打印支票: " + Data);
    }

    public void Save(string filePath)
    {
        File.WriteAllText(filePath, Data);
        Console.WriteLine("支票已保存: " + filePath);
    }

    public bool Validate()
    {
        return !string.IsNullOrEmpty(Data);
    }
}

这种方式让你可以随便扩展架构,类需要啥角色就加啥接口。要加新“职责”——直接实现新接口就行。

8. 常见错误和坑

新手常犯的错:忘了实现接口的所有成员。编译器绝不放过你——直接报错,告诉你少了啥。遇到这种错很好理解:“类必须实现接口成员”。

第二个常见问题——方法可见性搞混了。如果你用了显式实现,这个方法不能通过类变量访问——只能通过接口变量。

还有一个常见情况——重构时:你改了接口(比如加了个方法),但没更新所有实现类。这样就会编译报错。所以设计接口时,尽量别在很多类用上之后再改。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION