13接口【显式接口实现】

c#中的接口,有隐式接口【就是一般意义上直接实现某个接口中的方法,从表现上看,没有接口名称限定】,和显式接口【有接口名称限定】

internal interface IDemo{
    void func();
}

internal sealed class Demo : IDemo{
    //隐式实现接口
    public void func(){
        ...
    }

    //显式接口【注意没有public/private修饰】
    void IDemo.func(){
        ...
    }
}

一般说来,想当然的,我们认为如果一个类同时实现了多个接口【而这些接口中恰有一些,它们有着相同的函数签名】,那么我们需要显式实现这些接口【也就是用接口类型名称限定】这是没有错的。但是不仅仅如此,看下面这个例子,如果觉得有点“意思”再继续细究它:

    internal interface ITest{
        void func();
    }

    internal sealed class Test : ITest{
        void ITest.func(){
            ...
        }
    }

    //Main

    Test t = new Test();
    t.func();

这段代码中有问题吗?它是无法通过编译的。
错误: “Test”不包含“func”的定义,
并且找不到可接受类型为“ConsoleApplication3.Demo”的第一个参数的扩展方法
“func”(是否缺少 using 指令或程序集引用?)

为什么会这样?Test中不是明明实现了func吗?这里应该引起我们的注意【c++/java语言中没有显式接口这种概念】
如果写成:

ITest t = new Test();
t.func();
//或者
Test t = new Test();
((ITest)t).func();

又正常了。

再看一段:

internal interface ITest{
        void func();
}

internal sealed class Test : ITest{
    void ITest.func(){
        System.Console.WriteLine("interface ITest func");
    }


    public void func(){
        System.Console.WriteLine("class Test func");
    }
}
//Main

ITest it = new Test();
it.func()//输出 interface ITest func

Test t = new Test();
t.func();//输出 class Test func

希望这样的结果不会让【习惯了java/c++的人】觉得诧异。

再回头仔细看看,发现,对于接口的显式实现,没有用public/private等进行修饰【如果修饰了,无法通过编译】。原因是: 编译器在生成方法的元数据时,将其可访问性设为private,从而防止其他代码在使用类的实例时直接调用接口方法。要调用接口方法,只能通过接口类型的一个变量来进行。【这是强制”面向接口编程“吗- - ,不过感觉确实不咋地】。并且不能用virtual来修饰显式接口实现【显式接口实现并非真的是类型的对象模型的一部分,它是将一个接口(一组行为或者方法)连接到一个类型上,同时避免公开行为/方法的一种方式。

内部原理是:类型加载到CLR中时,会创建一个方法表,类型引入的每个新方法都有对应的记录【同时也为继承/实现的方法添加了记录】,那么当我们只进行了隐式实现时,接口方法【声明】记录和类中新方法【实现】 都指向了这个新方法实现。【从而导致,无论是使用接口类型还是实现类类型,调用的方法结果一样】,但是,如果此时进行了显式接口实现,那么接口方法就会指向这个显式实现,而类中隐式实现的【新方法】指向的仍然是隐式实现。【这就能理解上面这个例子的输出了,有点绕,- - 】

如果,我是说如果,一个类实现了多个接口,并且多个接口中有相同的签名【返回值,函数名,参数一致】,那么这种显式接口实现语义无可厚非【也只能显式接口实现,此时如果也进行了隐式接口实现,那么这个隐式实现是可有可无的(显式接口实现必须有),如果用实现类型去调用,就会调用这个隐式实现(如果你实现了的话)】,但是如果仅仅是实现一个接口,那么这种语义确实很容易让人慌乱。

如果一个类只实现一个接口,那么干嘛使用显式接口实现啊,直接用隐式实现不就可以了吗?也不尽然,即使只是实现一个接口,有时候也是需要使用到显式接口实现的。
看个例子:

public interface IComparable{
    int CompareTo(Object o);
}

//像下面这样定义一个类型去实现它

internal struct SomeValueType : IComparable{
    private int m_x;
    public SomeValueType(int x){this.m_x = x;}

    public int CompareTo(Object o){
        return (m_x - ((SomeValueType)other).m_x);
    }
}

//Main

SomeValueType v = new SomeValueType();
Object o = new Object();

int n = v.CompareTo(v);//会发生装箱操作
n = v.CompareTo(o);//运行时异常【object无法转型为SomeValueType】

上面,一个是发生了装箱操作【注意,SomeValueType是struct不是class 定义的】
还有就是运行时异常。这两点确实不怎么好。那么就可以用显式接口实现来试试看:

internal struct SomeValueType : IComparable{
    private int m_x;
    public SomeValueType(int x){this.m_x = x;}

    public int CompareTo(SomeValueType other){
        return (m_x - other.m_x);
    }
    //由于上面那个修改了参数类型,所以还需要实现接口【显式实现】
    //我自己无意尝试了下,如果隐式实现呢,那么是实现了接口的方法,可以通过编译
    //但是就没有什么意义了【因为对于传入Object类型,依然会调用(如果实现)这里
    //的隐式接口【还是会发生编译时异常】也就无法体现在这里显式接口实现的好处了。
    int IComparable.CompareTo(Object o){
        //委托给上面那个CompareTo(SomeValueType)实现
        return CompareTo((SomeValueType)o);
    }
}

//Main【没有任何变化,为了方便阅读和连贯,直接复制了一份】

SomeValueType v = new SomeValueType();
Object o = new Object();

int n = v.CompareTo(v);//不会发生装箱操作【调用参数为SomeValueType那个】

n = v.CompareTo(o);//编译时异常【object无法转型为SomeValueType】
//是显式实现接口【如果要调用接口方法,是需要使用接口类型变量的,
//而这里的v是SomeValueType,如果还不理解,看看本篇前面部分】

也可以通过泛型接口来实现【参见12 泛型】,这种实现本篇中就不做介绍了。

最后要注意的是:如果可能的话,尽量少用显式接口实现【毕竟语义不是那么明显】

c#编译器要求用于实现接口的方法标记为public,CLR会要求标记为virtual,如果在源代码中没有显式的标记virtual,那么编译器会标记为virtual和sealed【也就是派生类无法再重写这个实现类的这个方法】,如果源代码中自己标记了virtual【同时注意,此时不要让实现类有sealed修饰,否则出错】,那么方法就是非sealed【派生类可以重写】—-这里说的是对于接口的隐式方法实现和前面【显式接口实现是不允许标记virtual的】不冲突

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值