簡體   English   中英

如果使用接口應該一個類總是嚴格實現一個接口

[英]if using an interface should a class always strictly implement an interface

提出這個問題的更好方法是如下例子:這兩種方法的優點和缺點是什么? 一個總是優於另一個或在特定情況下? 如果使用Approach1,使用界面會沒有意義嗎? 既然任何人都可以訪問公共方法呢?

public interface IDoSomething
{
  void Method1(string operation, User user, string category)
  void Method2(string operation, User user)
  void Method3(string operation)
}

//Approach1
Class A: IDoSomething
{                              
  public void Method1(string operation, User user, string category)
  {
   //do some db logic here...
  }

  public void Method2(string operation, User user)
  {
    Method1(operation, user, "General");
  }

  public void Method3(string operation)
  {
    Method1(operation, User.GetDefaultUser(), "General");
  }
}

要么

//Approach2
Class A: IDoSomething
{                              
  void IDoSomething.Method1(string operation, User user, string category)
  {
   //do some logic here...
  }

  void IDoSomething.Method2(string operation, User user)
  {
    (this as IDoSomething).Method1(operation, user, "General");
  }

  void IDoSomething.Method3(string operation)
  {
    (this as IDoSomething).Method1(operation, User.GetDefaultUser(), "General");
  }
}

您正在尋找描述第二種方法的短語是顯式接口實現 就個人而言,我大部分時間都不會使用它。 您需要以不同的方式實現具有相同成員簽名的不同接口,或者如果您希望限制某些成員在處理接口類型的表達式時可見時,這很有用...但是如果對於無論你的調用者具有什么樣的具體類型,並且需要調用一些特定於類型的方法和一些接口方法。 如果不將this方法強制轉換為接口類型,您甚至無法在同一個類中看到顯式實現的方法。

顯式接口實現還意味着您不能覆蓋接口方法 - 您必須在任何子類中重新實現接口。 基本上它最終會變得非常復雜 - 所以如果可能的話我會避免它。

最后要注意的是:顯式接口實現在C#4中的dynamic很糟糕

方法1是在C#中實現接口的自然方式。 如果你寫一個圖書館,這就是你的客戶所期望的; 如果您自己使用該類,則在調用方法時可以節省大量的時間。

如果您有充分的理由 ,請使用方法2( 顯式接口實現 )。 好的理由可能包括

  • 實現多個接口共享相同的方法簽名(但需要不同的實現),
  • 方法名稱對接口有意義但可能會誤導您的類。

它(在某種程度上)是一種品味問題。 主要區別在於,如果進行顯式實現,除非首先將對象強制轉換為接口類型,否則無法調用這些方法:

A item = new A();
item.Method2(operation, user); // will not compile
(item As IDoSomething).Method2(operation, user); // works well

如果您不希望接口實現由於某種原因“混亂”IntelliSense中的成員列表,則此方法很有用。

我個人傾向於使用隱式方法,除非有特定的理由“隱藏”接口實現。

兩者的一個非常常見的組合是在實現IEnumerable<T> 由於IEnumerable<T>繼承自IEnumerable ,因此您必須實現IEnumerable<T>.GetEnumerator()IEnumerable.GetEnumerator() 由於它們具有不同的返回類型,但匹配的參數列表(空),最多只能隱式實現一個。

最常見的模式是實現通用版本:

public IEnumerator<T> GetEnumerator()
{
  //some code that returns an appropriate object.
}
IEnumerator IEnumerable.GetEnumerator()
{
  return GetEnumerator();//return the result of the other call.
}

現在,一個人不需要隱式,或者一個人可以(在顯式實現中使用一點點),反之亦然。 但是,由於以下原因,這很方便:

  1. 我們可能更喜歡調用隱式版本,因為如果通過更明確的接口處理枚舉器對象,它可能具有更好的類型安全性和更高的效率。
  2. 隱式版本將成為想要的兩個調用代碼IEnumerator<T>想要的和調用代碼IEnumerator (因為這是隱式轉換),並且因此是最有用的。

現在,那種情況是我們被迫使用顯式接口。 對於一個案例的例子,我們可以選擇,考慮編寫一個包含List<T>的只讀列表並實現IList<T>

我們所要做的就是將讀取操作委托給包裝列表,並且不支持寫入操作,例如:

public T this[int idx]
{
  get
  {
    return _inner[idx];
  }
  set
  {
    throw new NotSupportedException("Read only list.");
  }
}
public int Count
{
  get
  {
    return _inner.Count;
  }
}
public void Add(T item)
{
  throw new NotSupportedException("Read only list.");
}
public bool IsReadOnly
  {
  get
  {
    return false;
  }
}

等等。 但是,這對於使用具體類型的對象的人來說不是很有用,我們有很多成員要么返回相同的結果( IsReadOnly ),要么總是拋出異常。 雖然通過其接口使用類的代碼必須能夠調用這些方法和屬性,但在具體類中這樣做是沒有價值的。 因此我們可以這樣做:

public T this[int idx]
{
  get
  {
    return _inner[idx];
  }
}
T IList<T>.this[int index]
{
  get { return this[index]; }
  set { throw new NotSupportedException("Collection is read-only."); }
}
public int Count
{
  get
  {
    return _inner.Count;
  }
}
void ICollection<T>.Add(T item)
{
  throw new NotSupportedException("Read only list.");
}
bool ICollection<T>.IsReadOnly
  {
  get
  {
    return false;
  }
}

現在,當我們完全實現IList<T>我們還為具體類的用戶提供了一個更清潔,更有用的界面(在一般而不是c#特定的“界面”意義上)。

請注意,這正是基於它通常更有意義隱含的原因構建的:我們已經讓所有這些成員對於具體類的用戶來說更加尷尬(這樣的代碼必須首先強制轉換為IList<T> )。 這在這里很好,因為這種笨拙的代碼的唯一結果是毫無意義或危險,並且讓糟糕的想法更難做是一件好事。 如果這種尷尬只是阻礙了,那將是另一回事。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM