C#接口显示实现在实际开发中的作用
摘要
任何一个C#入门的程序员都知道——当一个类型在实现接口的时候,有两种方法实现:显式实现、隐式实现。而且大家也都知道,当一个类型实现的两个接口存在相同成员定义时,显示实现可以解决这种情况。
但是,在一个命名比较规范的项目中,几乎不可能出现上述情况。
那么,显示实现有什么具体存在的意义吗?
本人根据这小几年的开发历经,感觉显式实现最觉的两个作用就是:
- 改变接口成员的使用权限
- 改变接口成员的出入参数
下面本人将会对这两种作用进一一说明
接口定义
本篇文章将会使用一个示例来讲述这两个作用。
我们先来看一下示例吧:
- 定义一个接口类型,表示一个"元件",元件拥有两个成员
- 元件名称,主要是用来和其它元件进行区分
- 使用元件,需要一个Object类型的参数,表示将元件使用在哪个目标对象上
- 定义一个接口类型,表示一个"元件工厂",用来生成"元件"的实例,元件工厂拥有两个成员
- 根据指定的元件名称,生成一个元件实例
- 注册一个元件,只有被注册过的元件才能够被生产
例子还是挺简单的,我们来把这些接口类型码出来吧:
/// <summary>
/// 一个元件接口
/// </summary>
interface IComponent
{
/// <summary>
/// 元件的名称,用于区别于其它元件
/// </summary>
String ComponentName { get; } /// <summary>
/// 使用这个元件
/// <param name="target">对哪个对象使用这个元件</param>
/// </summary>
void Use(Object target);
} /// <summary>
/// 一个元件工厂接口
/// </summary>
interface IComponentFactory
{
/// <summary>
/// 创建元件
/// </summary>
/// <param name="componentName"></param>
/// <returns></returns>
IComponent CreateComponent(String componentName); /// <summary>
/// 注册一个元件
/// </summary>
/// <param name="component"></param>
void RegistComponent(IComponent component);
}
实现锁与钥匙的关系
光有接口,我们是无法工作的。因此我们开发一个叫钥匙的元件,再开发一个密码锁,钥匙可以用来开密码锁,当两者的编号相同时,锁才可以被打开。我们再做一个工厂,根据锁的编号生成一把钥匙,然后就可以开锁了。
我们先用隐式实现来做这件事
/// <summary>
/// 密码锁
/// </summary>
class PasswordLocker
{
/// <summary>
/// 开锁用的密码
/// </summary>
public String Code { get; set; }
} /// <summary>
/// 钥匙
/// </summary>
class Key : IComponent
{
/// <summary>
/// 对应的解锁密码,只有和锁的Code相同时才可以开锁
/// </summary>
public string Code { get; set; } public string ComponentName
{
get { return this.Code; }
} public void Use(object target)
{
//由于入参是继承了接口的Object类型,所以必须先对入参进行类型判断
if (target is PasswordLocker)
{
PasswordLocker pl = (PasswordLocker)target;
if (pl.Code == this.Code) Console.WriteLine("打开锁了");
else Console.WriteLine("未能把锁打开");
}
else
Console.WriteLine("目前类型不是锁,暂时不能使用该元件"); }
} /// <summary>
/// 钥匙工厂
/// </summary>
class KeyFactory : IComponentFactory
{
private Dictionary<String, IComponent> components = new Dictionary<string, IComponent>(); public IComponent CreateComponent(string componentName)
{
return components[componentName];
} /// <summary>
/// 由外部创建一个元件
/// </summary>
/// <param name="component"></param>
public void RegistComponent(IComponent component)
{
components[component.ComponentName] = component;
}
}
然后我们再写一代段码使用他们:
PasswordLocker locker = new PasswordLocker();
locker.Code = ""; Key k = new Key();
k.Code = "";
KeyFactory factory = new KeyFactory();
factory.RegistComponent(k);
factory.CreateComponent(locker.Code).Use(locker);
功能全部OK,但是有一些小小的瑕疵:
- 这把钥匙从代码上看,还可以用在不是PasswordLocker的类型上,所以Use方法中还要对入参类型进行判断;
- 明明是钥匙工厂,但从代码上看生产出来的依然只是元件接口类型,注册的时候也是,希能够直接使用钥匙类型;
- 如果我的元件工厂是能够自动在实例化的时候,从数据库里注册好所有元件了,那我自然不希望调用工厂的人可以继续Regist元件。
确实如此,当一个项目大到多人合作的时候,能够有着明确的入参与返回类型和私有化不需要其它人员调用的成员,是很有必要的一件事。
所以现在回头看刚才的那段实现代码,我们可以提出以下要求:
- 钥匙只能对锁进行Use
- 工厂从数据库中初始化所有钥匙,然后不再开放Regist方法
- 工厂生成出来的元件就是钥匙,而不是其它类型。
对新要求的实现:
我们通过对接口的显示实现,以保证接口成员不可以被显示的调用。再定义需要的public方法,调用接口成员,以达到上面三个要求:
/// <summary>
/// 这是一个仅仅能用于开密码锁的元件,使用了现式实现改变入参
/// </summary>
class PasswordLockerKey : IComponent
{
private string code; public PasswordLockerKey(String code)
{
this.code = code;
} string IComponent.ComponentName
{
get { return this.code; }
} void IComponent.Use(object target)
{
PasswordLocker pl = (PasswordLocker)target;
if (pl.Code == this.code) Console.WriteLine("打开锁了");
else Console.WriteLine("未能把锁打开");
} /// <summary>
/// 利用显示继承,隐藏了以Object作为入参的方法,并开放了一个仅仅以PasswordLocker作为入参的方法。改变了参数类型
/// </summary>
/// <param name="locker"></param>
public void User(PasswordLocker locker)
{
//将自身转化为接口类型,再调用Use才可以使用显式实现的方法
((IComponent)this).Use(locker);
}
} /// <summary>
/// 基于数据库的钥匙工厂,使用显式实现改变了接口成员的访问权限
/// </summary>
class DataBaseKeyFactory : IComponentFactory
{
private Dictionary<String, PasswordLockerKey> keys = new Dictionary<string, PasswordLockerKey>(); /// <summary>
/// 在构造函数的同时,从数据库中加载出所有钥匙
/// </summary>
public DataBaseKeyFactory()
{
IComponentFactory f = (IComponentFactory)this;
foreach (PasswordLockerKey k in LoadKeyFromDatabase())
{
f.RegistComponent(k);
}
} /// <summary>
/// 这是模拟的通过数据库加载钥匙的方法
/// </summary>
/// <returns></returns>
protected virtual IEnumerable<PasswordLockerKey> LoadKeyFromDatabase()
{
return new List<PasswordLockerKey>()
{
new PasswordLockerKey("")
};
} IComponent IComponentFactory.CreateComponent(string componentName)
{
return keys[componentName];
} void IComponentFactory.RegistComponent(IComponent component)
{
keys[component.ComponentName] = (PasswordLockerKey)component;
} /// <summary>
/// 这里改变了原本接口的返回类型
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public PasswordLockerKey CreateComponent(string code)
{
return (PasswordLockerKey)((IComponentFactory)this).CreateComponent(code);
}
}
代码中模拟了一下从数据库中取数的过程。
通过上面的方法,我们最终在使用这些类形的时候会发现,Use的入参对象只能是PasswordLocker,Factory不可以Regist成员了,Create时一定是PasswordLockerKey类型。已经基本满足上面的需要了。
小结
在实际项目中,其实这些现象依然是很少见的。往往出现在底层接口框架被搭建好后,对接口类型进行一次基础实现时,可能会遇到这些问题。
本人写此文章,希望大家在遇到类似问题的时候,能够想到可以使用接口的显示实现来解决。
希望能够大家带来一些提示和帮助。
文章为作者原创,转载请注明出处http://www.cnblogs.com/ShimizuShiori/p/5468749.html ,谢谢
C#接口显示实现在实际开发中的作用的更多相关文章
- Java动态加载类在功能模块开发中的作用
Java中我们一般会使用new关键字实例化对象然后调用该对象所属类提供的方法来实现相应的功能,比如我们现在有个主类叫Web类这个类中能实现各种方法,比如用户注册.发送邮件等功能,代码如下: /* * ...
- servlet接口实现类HttpServlet以及开发中一些细节
1. 但是eclipse不会帮我们改web.xml配置文件,所以我们也要在web.xml文件里面手动改 2. 这个样子的话你在用浏览器访问的时候链接的映射就改成了t_day05,这个主要用于你建立完一 ...
- 千位分隔符在web开发中的作用
有千位分隔符会被认为是数字,否则在移动端会被直接识别成手机号 在开发实战中主流实现方式是添加meta标签 <meta name="format-detection" cont ...
- JAVA中的垃圾回收机制以及其在android开发中的作用
http://blog.csdn.net/xieqibao/article/details/6707519 这篇文章概述了JAVA中运行时数据的结构,以及垃圾回收机制的作用.在后半部分,描述了如何检测 ...
- 【WebApi系列】浅谈HTTP在WebApi开发中的运用
WebApi系列文章 [01]浅谈HTTP在WebApi开发中的运用 [02]聊聊WebApi体系结构 [03]详解WebApi参数的传递 [04]详解WebApi测试和PostMan [05]浅谈W ...
- Android开发中,那些让您觉得相见恨晚的方法、类或接口
Android开发中,那些让你觉得相见恨晚的方法.类或接口本篇文章内容提取自知乎Android开发中,有哪些让你觉得相见恨晚的方法.类或接口?,其实有一部是JAVA的,但是在android开发中也算常 ...
- 支付宝(alipay)即时到账收款接口开发中的那些事儿
不会做,看看也可以会,要做好就还是需要多学习 国庆回来就一直没状态,看完<银河护卫队>,印象最深的竟然是只有两句台词的呆萌groot,昨天才休息一天,大耍大吃,今天还是把昨天的知识学习一下 ...
- 关于http接口开发中json格式数据编码问题处理
关于http接口开发中json格式数据编码问题处理 在实际工作中,接口很多时候返回json格式,但有时返回的格式会有编码问题 假设如下接口:http://service.test.com/interf ...
- IOS开发中如何判断程序第一次启动(根据判断结果决定是否显示新手操作引导)
IOS开发中如何判断程序第一次启动 在软件下载安装完成后,第一次启动往往需要显示一个新手操作引导,来告诉用户怎么操作这个app,这就需要在程序一开始运行就判断程序是否第一次启动,如果是,则显示新手操作 ...
随机推荐
- mysql 数据库迁移
公司的视频转码服务 使用mysql作为 任务队列, 其中mysql 是我们自己维护的 单例. 后来应业务部门建议,我们计划将现有的mysql 向dba进行迁移,以下记录一下 数据库迁移过程中的东西. ...
- QML Image: Cannot open: qrc:///new.pic.png
初次遇到这个问题真有点摸不着头脑,于是乎百度一下咯,但是百度一向没有什么用,该有的没有,没用的回答倒是有特么一大堆. 自己解决: 我的解决方法很简答: 第一步:把图片放到当前路径下,也就是和.pro一 ...
- CE创建进程和线程
创建进程: HWND hWnd = NULL; PROCESS_INFORMATION pi = {}; if(NULL==hWnd) { hWnd=FindWindow(NULL,_T(" ...
- Android——播放器和图片轮播
layout文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:an ...
- 关于Unity中SteamVR_Controller.Input的错误
当我在看某鸥的视频的时候,里面讲到用Unity做一个贪食蛇后,加入SteamVR插件,并且能用手柄控制蛇的移动.当我跟着上面一步一步做的时候,发现我代码都写完后,启动报错,而视频里面的老师讲的缺没有报 ...
- 一般处理程序上传文件(html表单上传、aspx页面上传)
html 表单上传文件 一般处理程序由于没有 apsx 页面的整个模型和控件的创建周期,而比较有效率.这里写一个用 html 表单进行文件上传的示例. 1. 表单元素选用 ...
- 基站查询接口,基站查询API
查询接口为商用,用于软件开发,非开发用户用不上.(说明:接口不能进行手机定位) 如需申请key,请联系QQ 2258172309. 1.移动联通基站查询接口 请求示例:http://www.cellm ...
- centos安装mongodb 3.2.9
centos 6.5 x64 1.下载地址:用迅雷下载,直接下载下不动 https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.2 ...
- LoadRunner简介
LoadRunner是什么 LoadRunner是一个性能测试工具,它最初是Mercury公司的产品,后背HP收购. LoadRunner常用来做什么 l 验证某系统在某环境下是否满足性能需求. l ...
- 学习地址(oraclemysqllinux)
1.安装配置 http://blog.chinaunix.net/uid-27126319-id-3466193.htmlhttp://www.cnblogs.com/gaojun/archive/2 ...