C# Interface的使用方法探讨

  接口是把公共实例(非静态)的方法和属性结合起来,以封装特定功能的一个集合,一旦定义了接口,就可以在类中使用实现接口中的所有成员,接口可以看作创建者和使用者之间的契约,一旦实现了接口,就不要轻易变动(如果需要变更接口,一般需要继承旧接口并且添加版本号)。我们知道在C++里面是有纯虚函数,虚继承和多重继承的,C#里面为了简化C++的这些复杂的设施,引出了接口这个概念。

C#接口和类的区别:



1. 不允许使用访问修饰符(public, private, protected,或者internal)修饰接口成员,所有的接口成员都是公共的。

2. 接口成员不能包含代码体

3. 接口不能定义字段成员

4. 不能用关键字static,virtual,abstract或者sealed来定义接口成员

5. 类型定义成员是禁止的。

实现C#隐式接口:


  其实接口和C++中那种头文件声明一个接口然后在cpp里面实现一遍那种做法看上去没有什么区别,只是C#把这个做的更纯粹(自从学了C#我越发觉得C++真是一门很啰嗦的语言)。如果一个类继承了一个接口,那么其对接口内的内容的实现可以在当前类实现,也可以在当前类的基类实现:

public class FuckBase
{
      public void FuckSomething(int fuck)
       {

       }
}

public class Fuck :FuckBase, A
{
        public int AInt
        {
            get;
            private set;
        }

        public void DoSomething()
        {

        }
}

  比如上面这个例子,就在基类中实现了接口,如果要隐藏基类的接口,可以直接new一下。

  当然了,接口是可以继承的,比如:

public interface A
{
    void DoSomething();
}

public interface DeriveedA: A
{
    new void DoSomething();
}

  

  在C#的接口中可以定义属性,比如:

public interface DeriveedA: A
{
    new void DoSomething();
    int AInt { get; set; }
}

  

  这样定义了以后,继承了DeriveedA的类必须实现AInt的get和set两个属性访问器了,并且都必须是public,有时候我们需要降低写访问器的访问权限,我们可以不在接口中定义set属性访问器,这样我们可以在类中实现有特殊访问属性的set属性访问器了,比如:

public interface DeriveedA: A
{
    new void DoSomething();
    int AInt { get; }
}

public class Fuck : DeriveedA
{
    public int AInt
    {
        get;
        private set;//当然了这里也可以是protected
    }

    public void DoSomething()
    {

    }
}

实现C#显式接口:



       上面的实现都属于C#的接口的隐式实现,那显式实现是什么东西?看下面的例子:

public class Starter
{
    /// <summary>
    /// 程序入口点
    /// </summary>
    /// <param name="args"></param>
    public static void Main(string[] args)
    {
        Fuck test = new Fuck();
    }
}

public interface IFuck
{
    void Haha();
}

public class Fuck :IFuck
{
    void IFuck.Haha()
    {

    }
}

 

  这个时候如果我们直接使用test对象,是无法调用Haha这个方法的,因为如果一个类显示实现了一个接口,那么这个接口函数将是private的,外部无法直接调用这个函数,除非把类显式转换为接口:

public class Starter
{
    /// <summary>
    /// 程序入口点
    /// </summary>
    /// <param name="args"></param>
    public static void Main(string[] args)
    {
        Fuck test = new Fuck();
        IFuck interfaceFuck = (IFuck)test;
        interfaceFuck.Haha();//这个时候相当于可以使用test.Haha这个方法了
    }
}

public interface IFuck
{
    void Haha();
}

public class Fuck :IFuck
{
    void IFuck.Haha()//注意显式实现接口不能带访问修饰符
    {

    }
}

  

  可能有人问为什么不把接口方法的实现定义为private,这个在C#里面是不允许的,如果一个类实现了一个接口(隐式实现),那么这个接口不能是private或者是protected的,必须是public的(必须是公共接口),这其实理解起来很自然,因为往往我们把一个类继承一个接口,就是需要这个接口所声明的方法。

  那么为什么还需要显式实现呢?从上面的例子我们可以看到,如果显式实现了一个接口,那么直接通过类来访问接口的方法是不行的,必须是显式转换为接口才可以访问,这就相当于了一个private的功能,比如我继承了一个IList<T>,我可能只需要IList<T>接口的一部分,那么我可以把我需要的部分隐式实现,不需要的部分显式实现,那么这个时候我既可以隐藏我不需要用到的方法,也可以把它当成一个IList<T>来用(用的时候转换为接口就好了)。这在软件工程里面是很常见的,有些时候我们写了一个类让框架来绑定,但是我们不想有些接口被误用,但是又想我们可以把它当做实现了这个接口的类型来用的时候,这样的做法就是最好的。非常符合面向对象的思想。

  再举几个可以用显示接口的例子。

  比如现在我有一个航空公司,公司里面有很多航班,但是其中B航班和C航班是特价航班,换句话说,就是航班之间的价格定价是不一样的,我们先假定一下所有航班的差别就是价格。

  那么我们很容易想到我们可以实现一个这样的航班类,但是我们如果要查询价格的时候,当我们显示查询B,C航班价格时,使用其各自特殊的计算方法,其他航班则选择统一的方法,在C#里面我们可以这样实现:

public class Starter
{
    /// <summary>
    /// 程序入口点
    /// </summary>
    /// <param name="args"></param>
    public static void Main(string[] args)
    {
        Flys fly = new Flys();
        IFlyB flyB = fly;
        flyB.Cost();//计算航班B的价格

        IFlyC flyC = fly;
        flyC.Cost();//计算航班C的价格

        fly.Cost();//计算普通航班的价格

        Console.ReadKey();
    }
}
public interface IFlyB
{
    void Cost();
}

public interface IFlyC
{
    void Cost();
}

public class Flys :IFlyB,IFlyC
{
    public void Cost()
    {
        Console.WriteLine("Other fly");
    }

    void IFlyB.Cost()
    {
        Console.WriteLine("Fly B");
    }

    void IFlyC.Cost()
    {
        Console.WriteLine("Fly C");
    }
}

  

  当然了,如果看过Effective C++的人已经知道我说的就是这本书上的例子,那么在C++的实现方法可以是实现一个Flys的虚基类,把Cost设为虚函数,然后派生出FlyC和FlyB,重写Cost,其他航班就显式使用虚函数的默认方法:

class Flys
{
public:
    virtual void cost()const = 0
    {
        std::cout << "Other fly" << std::endl;
    }
};

class FlyB :public Flys
{
public:
    void cost()const override
    {
        std::cout << "FlyB" << std::endl;
    }
};

class FlyC :public Flys
{
public:
    void cost()const override
    {
        std::cout << "FlyC" << std::endl;
    }
};

class OtherFly :public Flys
{
public:
    void cost()const override
    {
        Flys::cost();
    }
};

  

  这是一个C++的Best practice,因为这样写以后我们每次定义一种Fly都必须提供Fly的定义,可以使用默认定义,减少了程序员犯错的可能,C++可以这样写是因为C++的纯虚函数是一个很奇葩的东西,本身它应该是类似于C#的interface才对,但是却有了默认行为。

  第二个例子是,当我有两个接口,但是在一个在一个接口里面声明了名为Item的属性,另一个接口声明了Item的一个方法,如果我一个类要同时继承这两个接口,怎么办呢?

public interface IOne
{
    int Item { get; set; }
}

public interface ITwo
{
    int Item();
}

public class Hey : IOne, ITwo
{
    public int Item { get; set;}

    public int Item()
    {
        throw new NotImplementedException();
    }
}

  

  当你写出上面的代码的时候,编译器会报错,Item具有二义性,那么这个时候你必须显式实现一个接口:

public interface IOne
{
    int Item { get; set; }
}

public interface ITwo
{
    int Item();
}

public class Hey : IOne, ITwo
{
    public int Item { get; set;}

    int ITwo.Item()
    {

    }
}

  

  第三种情况就是刚才说的,当有些接口你不想让别人使用,但是你却想定义自己的版本的时候,比如你想实现一个List,并且想获得一个当Remove的时候还可以获取到节点的一个方法,但是IList<T>接口并没有声明这个方法,于是你可以这样写:

public class ListNode<T> : IList<T>
{
    public T RemoveAt(int index)
    {

    }

    void IList<T>.RemoveAt(int index)
    {

    }
}

  

  这样就实现了我们的想法,而且接口还不容易被误用。甚至你还可以在显式实现的接口void IList<T>.RemoveAt(int index)里面抛出异常,说明不支持这种方法。

时间: 2024-10-07 22:20:45

C# Interface的使用方法探讨的相关文章

TypeError: &#39;append&#39; called on an object that does not implement interface FormData 解决方法

使用ajax提交form表单时,$("formId").serialize()不能提交type="file"类型的input,这个时候可以选择使用FormData,使用方法如下 var dataForm = new FormData(document.getElementById("queryForm")); $.ajax({ processData: false,//这个必须有,不然会报错 contentType: false,//这个必须有,

网络信息安全风险态势预测分析方法探讨

1.研究背景 安全风险态势预测分析是信息安全技术和管理领域中的重要内容,传统的方法一般会按如下几个方面独立地或者混合进行分析: 1.获取历史上安全攻击相关信息,利用概率模型或者使用历史数据进行训练,根据结果进行风险预测[1] [2]: 2.根据各种信息资产的安全脆弱性进行分析: 3.根据各种信息资产的安全属性,包括保密性.完整性.可用性等: 4.根据网络拓扑模型进行分析,其手段主要是分析各种网络之间的关联关系,主要是联通性. 但上述分析方法显然存在一定问题,这主要表现在如下几个方面: 1.仅根据

关于anroid设置webview背景方法探讨(转)

最近的项目中一直关于webView设置背景色问题在研究,最终找到了解决的方法. 基于我项目的需求,从服务端传过来的是带有标签的文本,如果使用textView会让整个布局显得很乱,里面的<img />的加载也会是个麻烦,但是基于webView背景色的设置就是研究的一个方向了! 使用android 中WebView的同事们都会发现一个问题,在使用webView控件时会发现其背景色是系统固定好的,如果不是用特殊方法更改其背景色会和自己整体的布局及色彩搭配 很不适应! 下面就来介绍在不同android

as3.0 interface接口使用方法

[转]as3.0 interface接口使用方法 AS在2.0的时候就支持接口了 接口能够让你的程序更具扩展性和灵活性,打个例如 比方你定义了一个方法 代码: public function aMethod(arg:MyClass):void { .....} 參数 arg 的类型必须是 MyClass,由于我们须要在该方法中使用MyClass的API 而MyClass是一个类的话,你传入的对象必须是MyClass类的实例或者是MyClass子类的对象,可是当你要传入的一个对象,他既不是MyCl

提高软件测试效率的方法探讨

摘要:有位大师曾经问我,如何快速发现软件中的BUG?在当时有限时间情况下,我只说了测试者经验.熟悉需求等几个方面,显示这样的回答没能令他满意.软件测试有无银弹?有无高效的测试方法能尽快尽多发现软件中的缺陷?本文汲取部分软件测试工作者经验并结合笔者工作经历,就提高测试效率的一些方法进行归类探讨,以期和同行共享. 关键词:软件测试,效率 前言: 软件缺陷暴露得越早,越能降低开发和维护成本.研究表明软件寿命周期中,暴露缺陷的阶段与修改缺陷产生的开发成本之间的对应关系如下图所示: 软件测试作为一项工作,

Android平台Camera实时滤镜实现方法探讨(八)--简单美颜滤镜

美颜包含磨皮.美白.瘦脸等效果,其中磨皮算法在很多博客中均有介绍 例如: 双指数边缘平滑滤波器用于磨皮算法的尝试 选择性模糊及其算法的实现 基于局部均方差相关信息的图像去噪及其在实时磨皮美容算法中的应用 导向滤波磨皮 递归双边滤波磨皮 以上博客均有相关代码/公式,经试验若选取合适参数均有不错的效果,可惜水平有限尚未在shader中实现不卡顿的实时效果~ 观察美图秀秀和华为自带相机等相机APP,发现实时美颜效果均不如PC端和手机端后处理,可能在这一领域目前解决办法不多或者需求不高吧. 下面就探讨简

Android平台Camera实时滤镜实现方法探讨(九)--磨皮算法探讨(一)

上一篇开头提到了一些可用于磨皮的去噪算法.以下我们实现这些算法而且观察效果,咱不考虑实时性的问题 本文首先探讨的首先是<基于局部均方差相关信息的图像去噪及其在实时磨皮美容算法中的应用> 该算法利用图像局部统计特性进行滤波处理,比如NXM像素的灰度图,首先计算点(i,j)所在窗体内(大小为(2n+1)(2m+1))的平均值m(i,j) 以及均方差: 得到加性去噪后的结果为: 当中: 1.依据原文提出的优化方法,首先是建立两个积分图,如图所看到的.点4的积分即为Sum(Ra)+Sum(Rb)+Su

Java的接口(interface)属性和方法的类型

接口的属性必须是public static final Type 接口的方法必须是public abstract Type 不管你是全写,或只写部分,系统都会自动按上面的要求不全 也就是说 接口中 所有的属性 都是 只能是 静态的常量              接口中 所有的方法都是 只能是 抽象方法

js 中读取JSON的方法探讨

方法一:函数构造定义法返回 var strJSON = "{name:'json name'}";  //得到的JSONvar obj = new Function("return" + strJSON)()  ;//转换后的JSON对象alert(obj.name);   //json name 方法二:js中著名的eval函数   //ie8及以下 无法使用var strJSON = "{name:'json name'}";//得到的JSO