C#中接口的显式实现与隐式实现及其相关应用案例

· 浏览次数 : 0

小编点评

**接口显式实现与隐式实现** 接口显式实现和隐式实现是 C# 中两种常用的实现接口的方式。 **接口显式实现** 1. 定义一个接口,接口中有两个方法。 2. 在接口中声明一个显式实现方法。 3. 在类中使用接口类型传递接口实例,然后调用显式实现方法。 **接口隐式实现** 1. 定义一个接口,接口中有两个方法。 2. 在接口中声明一个隐式实现方法。 3. 在类中使用抽象类实现接口,并对生命周期函数进行显式实现。 4. 子类可以继承接口,并对其生命周期函数进行实现。 **区别** * **接口显式实现**是明确地定义在接口中,而 **接口隐式实现**是通过抽象类实现的。 * **接口显式实现**允许子类直接调用接口方法,而 **接口隐式实现**则需要子类通过抽象类进行实现。 * **接口显式实现**通常用于需要在接口中定义方法时,需要进行性能考虑的情况下。 **示例** **接口显式实现** ```csharp public interface ICanSingSong { void SingJayChow(); void SingOther(); } ``` **接口隐式实现** ```csharp public interface ISingAllSong { SongLibrary songLibrary { get; } } public class SongLibrary { // 方法实现 } ``` **使用** ```csharp // 使用接口显式实现 var onlySingSunny = new OnlySingSunny(); onlySingSunny.SingSunny(); // 使用接口隐式实现 var onlySingRainbowNoCry = new OnlySingRainbowNoCry(); onlySingRainbowNoCry.SingRainbow(); ```

正文

C#中接口的显式实现与隐式实现

最近在学习演化一款游戏项目框架时候,框架作者巧妙使用接口中方法的显式实现来变相对接口中方法进行“密封”,增加实现接口的类访问方法的“成本”。

接口的显式实现和隐式实现:

先定义一个接口,接口中有这两个方法。

 public interface ICanSingSong
 {
     void SingJayChow();
     void SingOther();
 }

接下来我们让InterfaceDesignExample 继承该接口。使用常用的隐式实现方法来实现SingJayChow方法,在Start函数中直接可以调用,而使用显式实现的接口方法SingOther,则需要将类的实力转换为接口类型才可以调用。

这样相当于告诉类,ICanSingSong.SingOther()样式就是表明SIngOther是属于ICanSingSong接口中的“私有方法”。调用时候先要将类类型转换为接口。

 public class InterfaceDesignExample : MonoBehaviour,ICanSingSong
 {
     void Start()
     {
         //接口的隐式实现 可以直接调用
         SingJayChow();
         
         //显示调用则需要 准换成接口 调用
         //this.SingOther();   
         (this as ICanSingSong).SingOther();
     }

     /// <summary>
     /// 接口的隐式实现
     /// </summary>
     public void SingJayChow()
     {
         Debug.Log("你说家是唯一的城堡,随着稻香一路奔跑~");
     }

     /// <summary>
     /// 接口的显式实现
     /// </summary>
     void ICanSingSong.SingOther()
     { 
         Debug.Log("lalalalalalallalaal!");
     }
 }

image

我们这样做的目的之一就是为了增加类对接口中的一些方法的调用成本,和使用“private”修饰有类似效果,降低对方法乱用的可能。

接口-抽象类-子类 使用显式实现接口方法

同样,我们先声明一个接口:

//接口 Application
public interface IApplication
{
    void Start();
    void Update();
    void Destroy();

    void Test();
}

抽象类继承该接口,并对生命周期函数进行显式实现,而供子类继承实现的方法为OnXXX

//抽象类
public abstract class Application : IApplication
{
    //不希望子类去访问实现接口的方法
    //使用显式调用
    void IApplication.Start()
    {
    	OnStart();
    }

    void IApplication.Update()
    {
    	OnUpdate();
    }

    void IApplication.Destroy()
    {
    	OnDestroy();
    }

    public void Test()
    {
    Debug.Log("我是测试方法,隐式实现接口的方法,子类可以轻松访问我");
    }

    //希望子类的实现的方法
    public abstract void OnStart();
    public abstract void OnUpdate();
    public abstract void OnDestroy();
}

继承抽象类的子类对生命周期函数进行实现:

 //子类
public class SubApplication : Application
{
    public override void OnStart()
    {
        Test();
        //Start(); 情况会发生递归调用 造成堆栈溢出 而此方法使用现实实现 所以在子类中无法访问 避免情况发生
        Debug.Log("OnStart");
    }

    public override void OnUpdate()
    {
        Debug.Log("OnUpdate");
    }

    public override void OnDestroy()
    {
        Debug.Log("OnDestroy");
    }
}

最后我们调用函数:

//测试调用
//通过接口调用 显示实现的方法
IApplication application = new SubApplication();

application.Start();
application.Update();
application.Destroy();

//通过类 无法调用显示实现的方法 只能访问使用OnXXXX方法
Application application2 = new SubApplication();
application2.OnStart();
application2.OnUpdate();
application2.OnDestroy();

image

这样我们可以体会到接口作为高抽象层的存在,可以调用子类具体实现的生命周期函数,而子类却无法访问显示实现的接口中“私有”的生命周期函数。

接口--子接口--静态类拓展 实现对接口函数的访问修饰

咳咳~故事开始!

作为一个资深Jay迷,我同样认识一个自称曲库的小精灵【SongLibrary】,它最拿手的三首歌曲是晴天、彩虹和说好不哭.

 //曲库
 public class SongLibrary
 {
     public void SingSunny()
     {
    	 Debug.Log("晴天:刮风这天,我试着握你的手~");
     }
 		//彩虹
     public void SingRainbow()
     {
     	Debug.Log("彩虹:你要离开,我知道很简单~");
     }

    public void SingNoCry()
    {
    	Debug.Log("说好不哭:说好不哭让我走~");
    }
}

这个小曲库精灵居住在抽象出的留声机中,我可以通过留声机来和它交流播放对应的歌曲。

public interface ISingAllSong
{
	SongLibrary songLibrary { get; }
}

留声机上有三个按钮,对应播放歌曲,

播放晴天的按钮功能:

抽象出子接口来继承曲库接口,通过静态类拓展来调用曲库中对应方法播放歌曲。

public interface ISingSunny : ISingAllSong
{

}

//静态类拓展
public static class SingSunnyExtensions
{
    public static void SingSunny(this ISingSunny self)
    {
       self.songLibrary.SingSunny();
    }
}

同样的方式,两个子接口。两个继承子接口的静态类负责调用曲库中的方法:

    //彩虹
    public interface ISingRainbow : ISingAllSong
    {
        
    }
    
    //静态类拓展
    public static class SingRainbowExtensions
    {
        public static void SingRainbow(this ISingRainbow self)
        {
            self.songLibrary.SingRainbow();
        }
    }
    
    //说好不哭
    public interface ISingNoCry : ISingAllSong
    {
        
    }
    
    //静态类拓展
    public static class SingNoCryExtensions
    {
        public static void SingNoCry(this ISingNoCry self)
        {
            self.songLibrary.SingNoCry();
        }
    }

这样我们使用三个静态类来调用留声机【interface】中居住的精灵【class】的方法,实现三个按钮功能。

当我想听歌时候,我只需要按照我的需求,搭配继承对应的子接口即可播放对应的歌曲,不用怕我按下去的是晴天歌曲播放按钮而短路到播放其它的歌曲曲目。

这样保证,拿到对应的子按钮,只能播放对应的歌曲,保证曲目播放的有序性。

public class InterfaceRuleExample : MonoBehaviour
{
    public class OnlySingSunny : ISingSunny
    {
    	SongLibrary ISingAllSong.songLibrary { get; } = new SongLibrary();
    }

    public class OnlySingRainbowNoCry : ISingRainbow,ISingNoCry
    {
    	SongLibrary ISingAllSong.songLibrary { get; } = new SongLibrary();
    }
    void Start()
    {
        var onlySingSunny = new OnlySingSunny();
        onlySingSunny.SingSunny();
        //不能访问
        //onlySingSunny.SingRainbow()
        //onlySingSunny.SingNoCry();

        var SingRainbowNoCry = new OnlySingRainbowNoCry();
        SingRainbowNoCry.SingRainbow();
        SingRainbowNoCry.SingNoCry();

        //无法访问
        //SingRainbowNoCry.SingSUnny();
    }
}

image

总结一下:

使用显示实现方式来对接口中方法进行实现,子类是无法直接调用的,需要将类转换为接口类型才可以调用。

同时如果接口中的方法不想让子类直接调用,可以让抽象类继承接口原生方法,在抽象类中进行方法声明供子类调用,避免子类对抽象层的直接交互。同时,使用静态类拓展来限制子接口对父接口中存在函数方法的访问,保证类对所需方法的规范使用。

也就是说,尽可能不让表层具象的类轻松的访问到抽象层的其它不需要的功能,即类需要什么就继承对应的子接口,实现对应功能即可,多余的功能不要访问。

当然也建议阅读一下官方社区对显式接口的实现的解释说明。

参考文章:

显式接口实现 - C# | Microsoft Learn

与C#中接口的显式实现与隐式实现及其相关应用案例相似的内容:

C#中接口的显式实现与隐式实现及其相关应用案例

C#中接口的显式实现与隐式实现 最近在学习演化一款游戏项目框架时候,框架作者巧妙使用接口中方法的显式实现来变相对接口中方法进行“密封”,增加实现接口的类访问方法的“成本”。 接口的显式实现和隐式实现: 先定义一个接口,接口中有这两个方法。 public interface ICanSingSong

Clear Code for Minimal API

我在写MinimalAPI的时候,发现不能最清晰的看到每个API,原因就是:WebAPI中不断增长逻辑处理过程 于是我在想如何简化API至一行,在一点一点想办法中,发现了简化DotNET Minimal API的方式。特此记录下来这个思路给需要帮助的人。我的灵感来源于 C# 11 功能 - 接口中的

C#中关于 object,dynamic 一点使用心得

首先说一下使用场景 WebAPI接口入参使用 object和 dynamic 后续解析和处理 1.object和dynamic 区别 在.NET中,object和dynamic也有一些区别: object:object是.NET中的顶级类,所有类都是object的子类。在C#中,您可以使用objec

C#反射实现插件式开发

前言 插件式架构,一种全新的、开放性的、高扩展性的架构体系。插件式架构设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现。扩展功能与框架以一种很松的方式耦合,两者在保持接口不变的情况下,可以独立变化和发布。基于插件设计并不神秘,相反它比起一团泥的设计更简单,更容易理解。

开源.NetCore通用工具库Xmtool使用连载 - 加密解密篇

【Github源码】 《上一篇》详细介绍了Xmtool工具库中的正则表达式类库,今天我们继续为大家介绍其中的加密解密类库。 在开发过程中我们经常会遇到需要对数据进行加密和解密的需求,例如密码的加密、接口传输数据的加密等;当前类库中只封装了Base64、AES两种加密解密方法,因为C#提供了几乎我们能

.NET周刊【6月第4期 2024-06-23】

国内文章 C#.Net筑基-集合知识全解 https://www.cnblogs.com/anding/p/18229596 .Net中提供了数组、列表、字典等多种集合类型,分为泛型和非泛型集合。泛型集合具有更好的性能和类型安全性。集合的基础接口包括IEnumerator、IEnumerable、I

三个编程思想:面向对象编程、面向接口编程、面向过程编程【概念解析系列_1】【C# 基础】

对于 .Net 中的编程思想还是十分重要的,也是编码出高效的程序的基础!

简易的工厂设计模式

工厂设计模式是一种创建型设计模式,它提供了一种创建对象的最佳方式,而无需暴露对象的创建逻辑。在工厂模式中,我们定义一个接口或抽象类,该接口或抽象类用于创建对象,但让子类决定要实例化的类。工厂方法模式使类的实例化延迟到其子类。 下面是一个完整的C#实现案例: 首先,我们定义一个接口,用于创建对象: p

万物皆可集成系列:低代码对接Web Service接口

我们知道活字格支持不写代码实现双向API绑定,那么没那么主流的Web Service接口(SOAP协议+XML交互格式)呢?其实对接的思路没有那么复杂,得用C#编码来对接的。 作为一款企业级低代码开发平台,活字格可以和许多第三方软硬件进行集成,灵活、高效使它不可忽视的优势。在之前的内容中我们已经介绍

6.0 Python 使用函数装饰器

装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为`"装饰器"(Decorator)`,装饰器的功能非常强大,装饰器一般接受一个函数对象作为参数,以对其进行增强,相当于C++中的构造函数,与析构函数。装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有迫切需求的场