我知道我们不能创建Interface的实例。但是为什么我们可以在左边写Interface?这些只是接口的引用吗(对于实现该类的类)而不是实例?
如果我们不能创建接口实例,那么这个例子中的P是什么类型?
1 2
| string[] names = {"John","Bob","Mark"};
IEnumerable<string> P = names.Where(n => n.Contains('o')); |
- "如果我们无法创建接口实例,那么这个示例中的p是什么类型?"我们不需要知道,它是一种接口点。它是实现IEnumerable的类型。精确的类型对用户来说并不重要。
- 您可能需要阅读以下内容:stackoverflow.com/questions/6802573/&hellip;
P为IEnumerable型。
这里声明局部变量P的类型。
P持有IEnumerable的类型,当您不直接实例化接口时,您可以实例化一个具体的类,但只持有对接口的引用。
Where()调用返回的实例只需要遵守IEnumerable定义的契约。
不能创建接口的实例,但可以创建实现接口的某个实例。
您在示例中所做的只是获取一些(P的内容),这些内容是已知的,可以遵守IEnumerable定义的合同。
如果用稍微简单的术语来思考这个概念,就更容易理解了:
1
| IBeast mrJuggles = new Tiger (); |
胡格斯先生现在是一只老虎,但同时也是一只江户(15岁)。我们没有明确地创建一个IBeast,我们只是指定我们所创建的东西将表现为一个IBeast,因此我们可以将它视为一个IBeast。
我将试着从概念上解释这一点。
but why we can write Interface on left side?
我们可以,因为它的意思是:"这个对象是实现这个接口的东西"。
接口本身不是"某物"——这就是为什么你不能直接实例化它——它只是一个契约。
接口是无用的,除非您定义了一些保证实现它所表示的契约的对象(并公开此类方法等)。这就是你对接口的使用。没有这个,他们就没有意义了。
合同本身是一个抽象概念。它需要一些东西来体现它——一个充满它的对象。
请看下面的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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:
1
| IEnumerable <string> foo = new Example ().AsEnumerable(); |
实际上意味着:"foo是我们知道实现IEnumerable的东西"。
但仍然是这样。它不仅可以是IEnumerable,而且只能是。IEnumerable只是我们碰巧知道的一些事情。
否则我们就不能把它扔回List,对吗?(这实际上是C中的一个常见警告,因为调用者可以进行这种转换,因此可以访问Add和Remove方法,并干扰我们列表的内容,即使我们不打算这样做。这是封装泄漏)。
换言之:我们看待这个对象的方式是IEnumerable。
编辑:
正如@kjartan建议的那样,您可以这样验证这一切:
1 2 3 4
| 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 |
- 实际上,我一直认为它是相反的;foo是IEnumerable,因为它具有定义为一个的属性。这并不意味着它也不可能是别的东西。
- @我重新措辞了。
- 更好。:)ps:如果您添加一个像var isFooIEnumerable = (foo is IEnumerable)和var isBarList = (bar is List)这样的子句,并且两者都是相反的,那么在所有4种情况下都会得到true,您的代码可能会使这一点更加清楚。:)
在左侧声明变量的类型。
IEnumerable stringEnumerator
您告诉编译器,从现在起,在值stringEnumerator下,可以提供对字符串枚举器对象的空引用或引用。
所以当你有这样的表情
IEnumerable P = names.where(n => n.Contains('o'));
你说,创建一个变量P,它将代表IEnumerable,并将names.where(n => n.Contains('o'));的结果赋给它。
确切地说,如果赋值中的左侧部分被类型化为接口类型,则表示左侧部分(变量或类似变量)是对实现所述接口的类的任何实例的引用。
在本例中,P是对某个实现IEnumerable的内部框架类实例的引用。因为它是一些内部类(假定隐藏在可枚举的实现中),所以我们甚至没有访问类名的权限(因此无法声明该类型的任何变量)。事实上,微软可能正在改变由Where返回的每个.NET版本的实际类。然而,我们也不会注意到,也不必担心,因为我们需要知道的是,实现IEnumerable的某些内容将被返回,因此我们也只声明变量P引用实现IEnumerable的某些内容,而不是作为具体类。