2014-02-26 92 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'));