【问题标题】:Interface on left side左侧接口
【发布时间】:2014-03-29 09:56:27
【问题描述】:

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

如果我们不能创建Interface的实例,这个例子中P是什么类型的?

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

【问题讨论】:

  • "如果我们不能创建Interface的实例,这个例子中的P是什么类型?"我们不必知道,它是接口的point。它是一种实现 IEnumerable 的类型。确切的类型对用户来说并不重要。
  • 您可能想阅读以下内容:stackoverflow.com/questions/6802573/…

标签: c#


【解决方案1】:

P 的类型为 IEnumerable&lt;string&gt;

这里你声明了局部变量P的类型。

P 持有IEnumerable&lt;string&gt; 的类型,虽然您不直接实例化接口,但您可以实例化一个具体类,而只需持有对该接口的引用。

Where() 调用在此处返回的实例只需遵守IEnumerable&lt;string&gt; 定义的协定。

【讨论】:

    【解决方案2】:

    您不能创建接口的实例,但可以创建实现接口的实例。

    您在示例中所做的只是获取已知遵守IEnumerable&lt;string&gt; 定义的合同的东西 (p)。

    如果你用稍微简单的术语来思考这个概念,就会更容易掌握:

    IBeast mrJuggles = new Tiger();
    

    Mr.Juggles 现在是 Tiger - 同时也是 IBeast。我们没有显式创建IBeast,我们只是指定我们创建的东西将表现为IBeast,因此我们将能够将其视为IBeast

    【讨论】:

      【解决方案3】:

      我将尝试从概念上解释这一点。

      但是为什么我们可以在左侧写接口呢?

      我们可以,因为它意味着:“这个对象是某物,它实现了这个接口”

      接口本身不是“东西”——这就是为什么你不能直接实例化它——它只是一个合同

      一个接口是没有用的,除非你定义了一些保证实现它所表达的契约的对象(并公开这样那样的方法等)。这就是你对接口的使用。没有这个,他们就没有意义了。

      合约本身是一个抽象概念。它需要一些东西来体现它——一个能够满足它的对象。

      看看下面的例子:

      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&lt;string&gt; 在这一行:

      IEnumerable<string> foo = new Example().AsEnumerable();
      

      实际上的意思是:“foo 是 我们知道可以实现的东西 IEnumerable”。

      但它仍然是那个东西。它不能只是 IEnumerable 而已。 IEnumerable 只是我们碰巧知道的。

      否则我们无法将其转换回List&lt;string&gt;,可以吗? (这实际上是 C# 中的一个常见警告,因为调用者可以执行此转换,因此可以访问 AddRemove 方法并弄乱我们列表的内容,即使我们不打算这样做。这是封装泄漏) .

      换句话说:IEnumerable&lt;string&gt;我们看待这个对象的方式

      编辑:

      正如@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
      

      【讨论】:

      • 其实,我总是反过来想; foo 是一个IEnumerable,因为它恰好具有需要定义为一个的属性。这并不意味着它也不能是别的东西。
      • @Kjartan 我改写了。
      • 更好。 :) PS:如果你添加一个像var isFooIEnumerable&lt;string&gt; = (foo is IEnumerable&lt;string&gt;)var isBarList&lt;string&gt; = (bar is List&lt;string&gt;) 这样的子句,或者反过来,你会在所有4 种情况下得到true,你的代码可能会更清楚地说明这一点。 :)
      【解决方案4】:

      在左边你声明变量的类型。

      IEnumerable&lt;string&gt; stringEnumerator

      您告诉编译器,从此刻起,可以在值stringEnumerator 下显示空引用或对作为字符串枚举器的对象的引用。

      所以当你有一个像

      这样的表达式时

      IEnumerable&lt;string&gt; P = names.where(n =&gt; n.Contains('o'));

      你说,创建一个变量p,它代表IEnumerable&lt;string&gt;,并分配给它names.where(n =&gt; n.Contains('o'));的结果

      【讨论】:

        【解决方案5】:

        确切地说,如果赋值中的左侧部分被键入为接口类型,则意味着左侧部分(变量或此类)是对实现所述接口的类的任何实例的引用。

        在给出的示例中,P 是对实现IEnumerable&lt;string&gt; 的某个内部框架类的实例的引用。由于它是一些内部类(可能隐藏在Enumerable 的实现中),我们甚至无法访问类名(因此无法声明该类型的任何变量)。实际上,Microsoft 可能会在每个 .NET 版本中更改 Where 返回的实际类。但是,我们既不会注意到,也不必关心,因为我们只需要知道实现IEnumerable&lt;string&gt;something 将被返回,因此我们也只需声明变量P作为引用实现IEnumerable&lt;string&gt; 而不是具体类的something

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多