在codeplex中浏览ASP.NET MVC源代码时,我发现有一个类显式实现接口是很常见的。显式实现的方法/属性然后调用具有相同名称的另一个“受保护的虚拟”方法/属性。
例如,
public class MvcHandler : IHttpHandler, IRequiresSessionState
{
protected virtual bool IsReusable
{
get
{
return false;
}
}
bool IHttpHandler.IsReusable
{
get
{
return IsReusable;
}
}
}
我现在确定这种编程的好处是什么。对我来说,我更喜欢隐式实现接口IHttpHandler。
我想作者只是不希望 MvcHandler 拥有公共属性 IsResuable 。仅当 MvcHandler 的实例被视为 IHttpHandler 时,才能使用 IsReusable 属性。不过,我不确定作者为什么会这样。
有谁知道这种界面实现的更多好处?
答案 0 :(得分:14)
嗯,不是特定于MVC,但这种方法允许您保持核心公共API干净。如果存在具有相同名称的不同接口/等的风险也是有用的。签名,但意义不同。实际上这很少见。
它还允许您提供一个实现,您希望在子类中更改返回类型:
(为了简单而选择了ICloneable
- 不要因为它是一个定义不明确的界面而被挂起......更好的例子就是像DbCommand
之类的东西,这样做 - 但在一个简短的例子中更难以显示)
class Foo : ICloneable
{
public Foo Clone() { return CloneCore(); }
object ICloneable.Clone() { return CloneCore(); }
protected virtual Foo CloneCore() { ... }
}
class Bar : Foo
{
protected override Foo CloneCore() { ... }
public new Bar Clone() { return (Bar)CloneCore(); }
}
如果我们使用了公共虚拟方法,我们将无法在基类中override
和使用new
,因为您不是允许两者兼得:
class A
{
public virtual A SomeMethod() { ... }
}
class B : A
{
public override A SomeMethod() { ... }
//Error 1 Type 'B' already defines a member called 'SomeMethod' with the same parameter types
public new B SomeMethod() { ... }
}
使用受保护的虚拟方法,任何用法:
所有对具体类型都使用正确的CloneCore()
实现。
答案 1 :(得分:2)
如果一个类显式实现IFoo.Bar
,并且派生类需要IFoo.Bar
做一些不同的事情,那么派生类将无法调用该方法的基类实现。不重新实现IFoo.Bar
的派生类可以通过((IFoo)this).Bar()
调用基类实现,但是如果派生类重新实现IFoo.Bar
(因为它必须以改变它的行为)前面提到的调用将转到派生类重新实现,而不是基类实现。即使((IFoo)(BaseType)this).bar
也无济于事,因为转换对接口类型的引用将丢弃有关引用类型的任何信息(而不是实例实例的类型)。
具有显式接口实现除了调用受保护方法之外什么都不做可以避免这个问题,因为派生类可以通过覆盖虚方法来改变接口方法的行为,同时保留调用基本实现的能力,因为它认为合适。恕我直言,C#应该有一个显式的接口实现产生一个符合CLS的名称的虚拟方法,所以用C#编写一个明确实现IFoo.Bar
的类的衍生物的人可以说override void IFoo.Bar
,并且有人写作用其他语言可以说,例如Overrides Sub Explicit_IFoo_Bar()
;因为任何派生类都可以重新实现IFoo.Bar
,并且因为任何不重新实现IFoo.Bar
的派生类都可以自己调用它,所以我没有看到有任何有用的目的。明确的实施是密封的。
顺便说一下,在vb.net中,普通模式只是Protected Overridable Sub IFoo_Bar() Implements IFoo.Bar
,而不需要单独的虚方法。
答案 2 :(得分:1)
原因1的样本:
public interface IFoo
{
void method1();
void method2();
}
public class Foo : IFoo
{
// you can't declare explicit implemented method as public
void IFoo.method1()
{
}
public void method2()
{
}
private void test()
{
var foo = new Foo();
foo.method1(); //ERROR: not accessible because foo is object instance
method1(); //ERROR: not accessible because foo is object instance
foo.method2(); //OK
method2(); //OK
IFoo ifoo = new Foo();
ifoo.method1(); //OK, because ifoo declared as interface
ifoo.method2(); //OK
}
}