我将尝试从概念上解释这一点。
但是为什么我们可以在左侧写接口呢?
我们可以,因为它意味着:“这个对象是某物,它实现了这个接口”。
接口本身不是“东西”——这就是为什么你不能直接实例化它——它只是一个合同。
一个接口是没有用的,除非你定义了一些保证实现它所表达的契约的对象(并公开这样那样的方法等)。这就是你对接口的使用。没有这个,他们就没有意义了。
合约本身是一个抽象概念。它需要一些东西来体现它——一个能够满足它的对象。
看看下面的例子:
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