左侧接口

时间:2014-02-26 14:00:06

标签: c#

我知道我们无法创建Interface的实例。 但为什么我们可以在左侧写Interface?那些只是Interface的参考 (对于实现此类的类)而不是实例?

如果我们无法创建Interface实例,那么此示例中的P类型是什么?

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

5 个答案:

答案 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#中的一个常见警告,因为调用者可以执行此操作,因此可以访问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

答案 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而不是具体的类。