2014-02-26 56 views
3

我知道我們無法創建Interface的實例。 但爲什麼我們可以在左側寫Interface?那些僅僅是Interface (對於實現這個的類)的引用而不是實例?左側的接口

如果我們不能創建接口的實例,那麼這個例子中的類型是P

string[] names = {"John", "Bob", "Mark"}; 
IEnumerable<string> P = names.Where(n => n.Contains('o')); 
+1

導致「如果我們不能創建的實例接口,這個例子中P是什麼類型?「我們不必知道,它是接口的*點*。它是一種實現IEnumerable 的類型。精確的類型對用戶無關緊要。 – dureuill

+0

你可能想讀這個:http://stackoverflow.com/questions/6802573/c-sharp-interfaces-whats-the-point – Paddy

回答

6

P的類型是IEnumerable<string>

在這裏,您在聲明局部變量P的類型。

P包含IEnumerable<string>的類型,雖然您沒有直接實例化接口,但可以實例化一個具體類,但只需保存對接口的引用。

Where()調用返回的實例需要僅遵守IEnumerable<string>定義的合同。

0

確切地說,如果賦值中的左手邊部分被鍵入爲接口類型,則意味着左手邊部分(變量或這樣的)是對實現所述接口的類的任何實例的引用。

在本示例中,P是對實施IEnumerable<string>的某些內部框架類的實例的引用。由於它是一些內部類(可以隱藏在Enumerable的實現中),我們甚至無法訪問類名(因此無法聲明該類型的任何變量)。事實上,微軟可能會改變每個.NET版本返回的實際類Where。然而,我們也不會注意到,因爲我們需要知道的是將實現IEnumerable<string>將返回,因此我們也只是聲明變量P作爲參考東西實現IEnumerable<string>而不是而不是具體的課程。

3

你不能創建一個接口的實例,但你可以創建一個實現它的實例。

你在你的例子中所做的只是獲取已知遵守IEnumerable<string>定義的合同的東西(p)。

它很容易,如果你認爲它掌握的概念稍微簡單來說:

IBeast mrJuggles = new Tiger(); 

Mr.Juggles現在是泰格 - 也是一個IBeast。我們沒有明確地創建IBeast,我們只是指定我們創建的東西將表現爲IBeast,因此我們可以將其視爲IBeast

2

我會試着從概念上解釋這一點。

但爲什麼我們可以在左側寫Interface?

我們可以,因爲這意味着:「這個對象是東西實現此接口」

接口本身不是「某些東西」 - 這就是爲什麼你不能直接實例化它 - 它只是一個合同

一個接口是沒用的,除非你定義了一些保證實現它所表示的合約的對象(並且公開這樣或那樣的方法等)。這就是你使用接口的用法。沒有這個,他們就沒有任何意義。

合同本身是一個抽象的概念。它需要一些東西來體現它 - 一個充滿了它的物體。

看一看下面的例子:

using System.Collections.Generic; 

namespace App 
{ 
    class Program 
    { 
     class Example 
     { 
      List<string> list = new List<string> { "a", "b", "c" }; 

      public IEnumerable<string> AsEnumerable() 
      { 
       return list; 
      } 
     } 

     static void Main(string[] args) 
     { 
      IEnumerable<string> foo = new Example().AsEnumerable(); 
      List<string> bar = (List<string>)foo; 
     } 
    } 
} 

你知道它不會崩潰?

在這一行IEnumerable<string>

IEnumerable<string> foo = new Example().AsEnumerable(); 

實際上意味着:「富是我們知道實現IEnumerable的東西」。

但它仍然是東西。它不能只有IEnumerable而只是。 IEnumerable只是我們碰巧知道的東西。

否則,我們不能將它扔回List<string>,可以嗎? (這實際上是C#中的一個常見警告,因爲調用者可以執行此操作,因此可以訪問AddRemove方法,並與我們的列表中的內容混雜在一起,即使我們不打算這樣做,這是封裝泄漏)。

換句話說:IEnumerable<string>我們看這個物體​​的方式

編輯:

由於@Kjartan建議,你可以驗證這一切就像這樣:

bool isFooIEnumerable = foo is IEnumerable<string>; // it's true 
bool isBarIEnumerable = bar is IEnumerable<string>; // true again 
bool isFooList = foo is List<string>; // yup. true 
bool isBarList = bar is List<string>; // true all the way 
+1

其實,我總是通過其他方式; 'foo'是一個'IEnumerable',因爲它具有需要被定義爲一個的屬性。這並不意味着它不能成爲別的東西。 – Kjartan

+1

@Kjartan我改口了。 –

+0

更好。 :) PS:如果添加'var isFooIEnumerable''這樣的子句'(foo是IEnumerable )和'var isBarList ='(bar是列表)',並且反過來也會得到'true '在所有4種情況下,你的代碼可能會使這一點更加清晰。 :) – Kjartan

1

在聲明變量的類型左側。

IEnumerable<string> stringEnumerator

你告訴從下價值stringEnumerator這一刻可以呈現一個空引用或參考對象,它是一個字符串枚舉的編譯器。

所以,當你有一個像

IEnumerable<string> P = names.where(n => n.Contains('o'));

表達你說,創建一個變量p將代表IEnumerable<string>,並分配給它的names.where(n => n.Contains('o'));