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!");
}
}

我们这样做的目的之一就是为了增加类对接口中的一些方法的调用成本,和使用“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();

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

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

咳咳~故事开始!

作为一个资深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();
}
}

总结一下:

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

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

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

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

参考文章:

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

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

  1. selenium-webdriver中的显式等待与隐式等待

    在selenium-webdriver中等待的方式简单可以概括为三种: 1 导入time包,调用time.sleep()的方法传入时间,这种方式也叫强制等待,固定死等一个时间 2 隐式等待,直接调用i ...

  2. C# Interface显式实现和隐式实现

    c#中对接口的实现方式有两种:隐式实现和显式实现,之前一直没仔细看过,今天查了些资料,在这里整理一下. 隐式实现的例子 interface IChinese { string Speak(); } p ...

  3. Scala 中的隐式转换和隐式参数

    隐式定义是指编译器为了修正类型错误而允许插入到程序中的定义. 举例: 正常情况下"120"/12显然会报错,因为 String 类并没有实现 / 这个方法,我们无法去决定 Stri ...

  4. C# 数据类型转换 显式转型、隐式转型、强制转型

    C# 的类型转换有 显式转型 和 隐式转型 两种方式. 显式转型:有可能引发异常.精确度丢失及其他问题的转换方式.需要使用手段进行转换操作. 隐式转型:不会改变原有数据精确度.引发异常,不会发生任何问 ...

  5. (java)selenium webdriver学习---三种等待时间方法:显式等待,隐式等待,强制等待

    selenium webdriver学习---三种等待时间方法:显式等待,隐式等待,强制等待 本例包括窗口最大化,刷新,切换到指定窗口,后退,前进,获取当前窗口url等操作: import java. ...

  6. Java并发之显式锁和隐式锁的区别

    Java并发之显式锁和隐式锁的区别 在面试的过程中有可能会问到:在Java并发编程中,锁有两种实现:使用隐式锁和使用显示锁分别是什么?两者的区别是什么?所谓的显式锁和隐式锁的区别也就是说说Synchr ...

  7. Scala 深入浅出实战经典 第61讲:Scala中隐式参数与隐式转换的联合使用实战详解及其在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...

  8. 多态设计 zen of python poem 显式而非隐式 延迟赋值

    总结 1.python支持延迟赋值,但是给调用者带来了困惑: 2.显式而非隐式,应当显式地指定要初始化的变量 class Card: def __init__(self, rank, suit): s ...

  9. 大数据技术之_16_Scala学习_06_面向对象编程-高级+隐式转换和隐式值

    第八章 面向对象编程-高级8.1 静态属性和静态方法8.1.1 静态属性-提出问题8.1.2 基本介绍8.1.3 伴生对象的快速入门8.1.4 伴生对象的小结8.1.5 最佳实践-使用伴生对象解决小孩 ...

  10. Scala 学习之路(十三)—— 隐式转换和隐式参数

    一.隐式转换 1.1 使用隐式转换 隐式转换指的是以implicit关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能.示例如下: // 普通人 clas ...

随机推荐

  1. mysql 重新整理——索引简介[七]

    前言 百度百科索引: 在关系数据库中,索引是一种单独的.物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单. ...

  2. 向量数据库Chroma学习记录

    一 简介 Chroma是一款AI开源向量数据库,用于快速构建基于LLM的应用,支持Python和Javascript语言.具备轻量化.快速安装等特点,可与Langchain.LlamaIndex等知名 ...

  3. 力扣557(java)-反转字符串中的单词(简单)

    题目: 给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序. 示例 1: 输入:s = "Let's take LeetCode contest&qu ...

  4. HarmonyOS NEXT应用开发案例——列表编辑实现

    介绍 本示例介绍用过使用ListItem组件属性swipeAction实现列表左滑编辑效果的功能. 该场景多用于待办事项管理.文件管理.备忘录的记录管理等. 效果图预览 使用说明: 点击添加按钮,选择 ...

  5. InnoDB之UNDO LOG介绍

    简介: undo log是InnoDB事务特性的重要组成部分.当对记录做增删改操作就会产生undo记录,undo记录会记录到单独的表空间中. 本文将从代码层面对undo log进行一个简单的介绍:主要 ...

  6. 大数据时代下,App数据隐私安全你真的了解么?

    ​简介:你是否有过这样的经历:你和朋友聊天表达你近期想要购买某件商品,第二天当你打开某购物软件时,平台向你推送的商品正是你想要购买的:或者,你是否接到过陌生来电,他们准确的报出了你的名字和年龄.... ...

  7. hyengine - 面向移动端的高性能通用编译/解释引擎

    ​简介:手机淘宝客户端在历史上接过多种多样的脚本引擎,用于支持的语言包括:js/python/wasm/lua,其中js引擎接过的就有:javascriptcore/duktape/v8/quickj ...

  8. 探索 WPF 的 ITabletManager.GetTabletCount 在 Win11 系统的底层实现

    本文将和大家介绍专为 WPF 触摸模块提供的 ITabletManager 的 GetTabletCount 方法在 Windows 11 系统的底层实现 本文属于 WPF 触摸相关系列博客,偏系统底 ...

  9. MAUI 已知问题 PathFigureCollectionConverter 非线程安全

    在 MAUI 里,可以使用 PathFigureCollectionConverter 将 Path 字符串转换为 PathFigureCollection 对象,从而实现从 Path 字符串转换为路 ...

  10. Linux备忘手册

    资料来源:技术胖 jspang.com 下载linux学习路径:https://newimg.jspang.com/linux-image01.png Linux备忘手册: 百度网盘 链接:https ...