Effective C# 原則20:明辨介面實現和虛函數重載的區別(譯)

來源:互聯網
上載者:User

Effective C# 原則20:明辨介面實現和虛函數重載的區別
Item 20: Distinguish Between Implementing Interfaces and Overriding Virtual Functions

粗略的看一下,感覺實現介面和虛函數重載是一樣的。你定義了一些對象,但是這些對象是在另一個類型裡申明的。你被第一感覺騙了,實現介面與虛函數重載是完全不同的。在介面裡定義的成員預設情況下,是根本不存在實際內容的。

衍生類別不能重載基類中的介面成員。介面可以隱式的實現,就是把它們從類的公用介面中隱藏。它們的概念是不同的而且使用也是不同的。

但你可以這樣的實現介面:讓你的衍生類別可以修改你的實現。你只用對衍生類別做一個Hook就行了。(譯註:相信寫過C++程式的人就知道hook是什麼意思,而且我也實在想不到把hook譯成什麼比較好,所以就直接用hook這個原詞了,就像bug一樣。)

為了展示它們的不同之處,試著做一個簡單的介面以及在一個類中實現它:

interface IMsg
{
  void Message();
}

public class MyClass : IMsg
{
  public void Message()
  {
    Console.WriteLine( "MyClass" );
  }
}

Message()方法是MyClass的公用介面,Message同樣可以用一個介面指標IMsg來訪問。現在讓我們來一點繁雜的,添加一個衍生類別:

public class MyDerivedClass : MyClass
{
  public new void Message()
  {
    Console.WriteLine( "MyDerivedClass" );
  }
}

注意到,我添加了一個關鍵字new在Message方法上,用於區別前面的一個Message(參見原則29)。MyClass.Message()不是虛函數,衍生類別可以不提供重載版本。MyDerived類建立了一個新的Message方法,但這個方法並不是重載MyClass.Message:它隱藏了原來的方法。而且,MyClass.Message還是可以通過IMsg的引用來訪問:

MyDerivedClass d = new MyDerivedClass( );
d.Message( ); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message( ); // prints "MyClass"

介面方法不是虛的,當你實現一個介面時,你就要在詳細的相互關聯類型中申明具體的實現內容。

但你可能想要建立介面,在基類中實現這些介面而且在衍生類別中修改它們的行為。這是可以辦法到的。你有兩個選擇,如果不訪問基類,你可以在衍生類別中重新實現這個介面:

public class MyDerivedClass : MyClass, IMsg
{
  public new void Message()
  {
    Console.WriteLine( "MyDerivedClass" );
  }
}

添加的IMsg讓你的衍生類別的行為發生了改變,以至IMsg.Message現在是在衍生類別上使用的:

MyDerivedClass d = new MyDerivedClass( );
d.Message( ); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message( ); // prints "MyDerivedClass"

衍生類別上還是須要在MyDerivedClass.Message()方法上添加關鍵字new,這還是有一點隱患(參見原則29)。基類還是可以通過介面引用來訪問:

MyDerivedClass d = new MyDerivedClass( );
d.Message( ); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message( ); // prints "MyDerivedClass"
MyClass b = d;
b.Message( ); // prints "MyClass"

唯一可以修正這個問題的方法是修改基類,把介面的申明修改為虛函數:

public class MyClass : IMsg
{
  public virtual void Message()
  {
    Console.WriteLine( "MyClass" );
  }
}

public class MyDerivedClass : MyClass
{
  public override void Message()
  {
    Console.WriteLine( "MyDerivedClass" );
  }
}

MyDerivedClass以及其它所有從MyClass派生的類可以申明它們自己的Message()方法。這個重載的版本每次都會調用:通過MyDerivedClass的引用,通過IMsg介面的引用,或者直接通過MyClass的引用。

如果你不喜歡混雜的虛函數概念,那就對MyClass的定義做一個小的修改:

public abstract class MyClass, IMsg
{
  public abstract void Message();
}

是的,你可以用一個抽象方法來實現一個介面。通過申明一個介面內的抽象的方法,你可以讓你的所有派生都必須實現這個介面。現在,IMsg介面成為了MyClass的一個組成部份,你的每一個衍生類別都必須實現它。

隱式介面實現,可以讓你在一個類上隱藏公用的介面成員方法,而且也實現了這個介面。它在實現介面和虛函數重載上繞了幾個圈。當有多個合適的函數版本時,你可以利用隱式介面的實現來限制使用者的編碼。在原則26講到的IComparable習慣會詳細的討論這一點。

實現介面讓我們有更多的選擇,用於建立和重載虛函數。你可以建立隱秘的實現,虛的實現,或者抽象關聯到衍生類別。你可以精確的決定,你的衍生類別如何以及何時,修改介面的預設實現。介面方法不是虛方法,而是一個獨立的約定!
===============================
   

Item 20: Distinguish Between Implementing Interfaces and Overriding Virtual Functions
At first glance, implementing an interface seems to be the same as overriding avirtual function. You provide a definition for a member that has been declared in another type. That first glance is very deceiving. Implementing an interface is very different from overriding a virtual function. Members declared in interfaces are not virtualat least, not by default.

Derived classes cannot override an interface member implemented in a base class. Interfaces can be explicitly implemented, which hides them from a class's public interface. They are different concepts with different uses.

But you can implement interfaces in such a manner that derived classes can modify your implementation. You just have to create hooks for derived classes.

To illustrate the differences, examine a simple interface and implementation of it in one class:

interface IMsg
{
  void Message();
}

public class MyClass : IMsg
{
  public void Message()
  {
    Console.WriteLine( "MyClass" );
  }
}

 

The Message() method is part of MyClass's public interface. Message can also be accessed through the IMsg point that is part of the MyClass type. Now let's complicate the situation a little by adding a derived class:

public class MyDerivedClass : MyClass
{
  public new void Message()
  {
    Console.WriteLine( "MyDerivedClass" );
  }
}

 

Notice that I had to add the new keyword to the definition of the previous Message method (see Item 29). MyClass.Message() is not virtual. Derived classes cannot provide an overridden version of Message. The MyDerived class creates anew Message method, but that method does not override MyClass.Message: It hides it. Furthermore, MyClass. Message is still available through the IMsg reference:

MyDerivedClass d = new MyDerivedClass( );
d.Message( ); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message( ); // prints "MyClass"

 

Interface methods are not virtual. When you implement an interface, you are declaring a concrete implementation of a particular contract in that type.

But you often want to create interfaces, implement them in base classes, and modify the behavior in derived classes. You can. You've got two options. If you do not have access to the base class, you can reimplement the interface in the derived class:

public class MyDerivedClass : MyClass, IMsg
{
  public new void Message()
  {
    Console.WriteLine( "MyDerivedClass" );
  }
}

 

The addition of the IMsg keyword changes the behavior of your derived class so that IMsg.Message() now uses the derived class version:

MyDerivedClass d = new MyDerivedClass( );
d.Message( ); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message( ); // prints "MyDerivedClass"

 

You still need the new keyword on the MyDerivedClass.Message() method. That's your clue that there are still problems (see Item 29). The base class version is still accessible through a reference to the base class:

MyDerivedClass d = new MyDerivedClass( );
d.Message( ); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message( ); // prints "MyDerivedClass"
MyClass b = d;
b.Message( ); // prints "MyClass"

 

The only way to fix this problem is to modify the base class, declaring that the interface methods should be virtual:

public class MyClass : IMsg
{
  public virtual void Message()
  {
    Console.WriteLine( "MyClass" );
  }
}

public class MyDerivedClass : MyClass
{
  public override void Message()
  {
    Console.WriteLine( "MyDerivedClass" );
  }
}

 

MyDerivedClassand all classes derived from MyClasscan declare their own methods for Message(). The overridden version will be called every time: through the MyDerivedClass reference, through the IMsg reference, and through the MyClass reference.

If you dislike the concept of impure virtual functions, just make one small change to the definition of MyClass:

public abstract class MyClass, IMsg
{
  public abstract void Message();
}

 

Yes, you can implement an interface without actually implementing the methods in that interface. By declaring abstract versions of the methods in the interface, you declare that all types derived from your type must implement that interface. The IMsg interface is part of the declaration of MyClass, but defining the methods is deferred to each derived class.

Explicit interface implementation enables you to implement an interface, yet hide its members from the public interface of your type. Its use throws a few other twists into the relationships between implementing interfaces and overriding virtual functions. You use explicit interface implementation to limit client code from using the interface methods when a more appropriate version is available. The IComparable idiom in Item 26 shows this in detail.

Implementing interfaces allows more options than creating and overriding virtual functions. You can create sealed implementations, virtual implementations, or abstract contracts for class hierarchies. You can decide exactly how and when derived classes can modify the default behavior for members of any interface your class implements. Interface methods are not virtual methods, but a separate contract.

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.