我知道我们无法创建Interface
的实例。
但为什么我们可以在左侧写Interface
?那些只是Interface的参考
(对于实现此类的类)而不是实例?
如果我们无法创建Interface实例,那么此示例中的P
类型是什么?
string[] names = {"John", "Bob", "Mark"};
IEnumerable<string> P = names.Where(n => n.Contains('o'));
答案 0 :(得分:6)
P
的类型为IEnumerable<string>
在此声明局部变量P
的类型。
P
保存IEnumerable<string>
的类型,虽然您没有直接实例化接口,但您可以实例化一个具体类,但只需保存对该接口的引用。
Where()
调用返回此处的实例只需遵守IEnumerable<string>
定义的合同。
答案 1 :(得分:3)
您无法创建接口的实例,但可以创建实现的实例。
您在示例中执行的操作只是获取已知符合p
定义的合同的内容(IEnumerable<string>
)。
如果你用稍微简单的术语来理解它,就会更容易理解这个概念:
IBeast mrJuggles = new Tiger();
Mr.Juggles现在是老虎 - 但也是IBeast
。我们没有明确创建IBeast
,我们只是指定我们创建的内容将表现为IBeast
,因此我们可以将其视为IBeast
。
答案 2 :(得分:2)
我会尝试从概念上解释这一点。
但为什么我们可以在左侧编写接口?
我们可以,因为它意味着:“此对象某些实现此接口”。
接口本身并不是“某种东西” - 这就是为什么你不能直接实例化它 - 它只是一个合同。
除非你定义一些保证实现它所表达的契约的对象(并公开这样的方法等),否则接口是无用的。这就是你对接口的使用。没有这个,他们没有任何意义。
合同本身是一个抽象概念。它需要一些东西来体现它 - 一个充满它的对象。
看一下以下示例:
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();
实际上意味着:“foo是我们知道实现 IEnumerable”的东西。
但仍然是某事。它不能只是 IEnumerable 而已。 IEnumerable只是我们碰巧知道的事情。
否则我们无法将其强制转回List<string>
,是吗? (这实际上是C#中的一个常见警告,因为调用者可以执行此操作,因此可以访问Add
和Remove
方法并弄乱列表中的内容,即使我们打算不这样做。这是封装泄漏)。
换句话说: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
答案 3 :(得分:1)
在左侧,您声明变量的类型。
IEnumerable<string> stringEnumerator
您告诉编译器,从此时刻开始,值stringEnumerator
可以呈现空引用或对作为字符串枚举器的对象的引用。
所以当你有一个像
这样的表达式时 IEnumerable<string> P = names.where(n => n.Contains('o'));
你说,创建一个代表p
的变量IEnumerable<string>
并为其分配names.where(n => n.Contains('o'));
答案 4 :(得分:0)
确切地说,如果赋值中的左侧部分被键入为接口类型,则意味着左侧部分(变量等)是对实现所述接口的类的任何实例的引用。
在提供的示例中,P
是对实现IEnumerable<string>
的某个内部框架类的实例的引用。由于它是一些内部类(可能隐藏在Enumerable的实现中),我们甚至无法访问类名(因此无法声明该类型的任何变量)。实际上,Microsoft可能会更改Where
在每个.NET版本中返回的实际类。但是,我们也不会注意到,我们也不必关心,因为我们需要知道的是,将返回实现IEnumerable<string>
的 something ,因此我们也只是声明变量{ {1}}引用实现P
的而不是具体的类。