【问题标题】:Interface instances接口实例
【发布时间】:2010-09-16 18:10:08
【问题描述】:

我是 C#.net 的新手,很惊讶地知道可以像这样创建接口的实例

Iinterface myDimensions = (Iinterface) myBox;

如何为这种类型的语句分配内存?内存是在堆上分配的吗?

谁能给出使用这种类型实例化的任何情况。

实现接口的类可以显式实现该接口的成员。当成员显式实现时,不能通过类实例访问,只能通过接口实例访问。

为什么要在语言中强制执行这样的约束?

谢谢,

【问题讨论】:

    标签: c# .net interface


    【解决方案1】:

    在回答您的第一个问题之前,我注意到您有两个问题。将来,当您有两个问题时,请考虑在 Stack Overflow 上提出两个单独的问题。您可能已经注意到,当您同时提出两个问题时,通常几乎每个人都会忽略第二个问题。

    我很惊讶地知道接口的实例可以像这样创建

    Iinterface myDimensions = (Iinterface) myBox; 
    

    如何为这种类型的语句分配内存?内存是在堆上分配的吗?

    正如其他人所指出的,这不是必须创建实现接口的类型的实例。每个人似乎都忘记了告诉您引用转换不分配内存装箱转换确实分配内存。如果 myBox 是结构类型,那么这将在堆上为“包装器”分配内存并复制包装器中的值。然后包装器实现接口。

    继续你的第二个问题:

    实现接口的类可以显式实现该接口的成员。当显式实现成员时,不能通过类实例访问它,而只能通过接口的实例访问。为什么要在语言中强制执行这样的约束?

    显式接口实现的目的是让一个类实现一个特定的接口,而不要求这些方法出现在它们不需要的地方。例如,假设您有:

    class MyConnection : IDisposable
    {
        public void OpenConnnection() { ... }
        public void CloseConnection() { ... }
        public void Dispose() { ... CloseConnection(); ... }
    }
    

    如果处理打开的连接与关闭连接相同,那么您可能不希望通过 (1) 使用两个执行相同操作的方法或 (2) 将方法 OpenConnection 与不明显的名称,例如 Dispose。通过使您能够使 Dispose “不可见”,除非对象被转换为 IDisposable,然后您就可以让您的用户更容易发现正确的事情。

    另一种使用它的情况是当你有两个同名的接口时:

    interface IFoo { void M(); }
    interface IBar { void M(); }
    

    现在你如何创建一个实现 IFoo 和 IBar 的类 C,但对两个 M 方法有不同的实现?如果您想要两个不同的主体,则必须对其中一个或两个使用显式实现。

    【讨论】:

      【解决方案2】:

      这不是实例化——它是类型转换。该实例是原始对象,即myBox。至于内存,是的,为引用分配了内存——它是在堆上还是在堆栈上完全取决于上下文。在您的示例中(看起来像是在函数中),我猜是堆栈。

      关于显式实现:语言特性允许单个类实现两个或多个接口,其中包含一个或多个具有完全相同签名的成员。例如:

      interface A
      {
         void Foo();
      }
      
      interface B
      {
         void Foo();
      }
      
      class C : A, B
      {
         void A.Foo()
         {
         }
      
         void B.Foo()
         {
         }
      }
      

      如果没有这个特性,编译器就不清楚C.Foo 实现了哪个接口成员。当然,需要注意的是,调用者不能简单地调用C.Foo,因为调用哪个方法也不是很明显;因此,C 类型的对象必须首先转换为 AB,以明确程序员的愚蠢意图。

      【讨论】:

      • 嗯,在当前的 CLR 实现中,我相信无论 myBox 是否是值类型,myDimensions 都会被视为引用类型(因此是堆分配的)。因此,如果myBox 在堆栈上,myDimensions 将是它在堆上的盒装副本。
      • 你为什么要猜堆栈?
      • 我的意思是 reference 是在堆栈上分配的,而不是对象本身。确实,如果myBox是值类型,那么装箱值所需的内存将分配在堆上。
      • @Ben M 你能告诉我为什么会出现“显式实现成员时,不能通过类实例访问,只能通过接口实例访问”的约束。
      • @Sandeep 因为我上面提到的原因:如果你有var c = new C(); c.Foo();,不清楚你想调用哪个Foo——所以你必须进行类型转换。隐藏(几乎)是显式实现的全部要点:您不需要显式实现成员;它仅在这些情况下才真正有用。 (或者类似的情况,C : AC 已经有了Foo()。)
      【解决方案3】:

      你所拥有的是一个演员,而不是一个实例化。在这种情况下,必须在其他地方创建对象:

      // Allocate memory on the stack to point to the location on the heap
      // to store the object and create the object on the heap.
      Box box = new Box();
      
      // Allocate memory on the stack to point to the location on the heap
      // and point it to the already existing object on the heap.
      IInterface iBox = (IInterface)box;
      

      您从 MSDN 获得的引述是对 Explicit Interface Implementations 的引用。这些可能有点令人困惑。一个简单的例子是最简单的:

      public interface ISomething
      {
          void SayHi();
      }
      
      public class Something : ISomething
      {
          public void SayHi() { Console.WriteLine("Hello World!"); }
      
          public void ISomething.SayHi() { Console.WriteLine("42!"); }
      }
      

      现在您的代码可以具有以下内容:

      Something obj = new Something();
      
      // Outputs "Hello World!"
      obj.SayHi();
      
      ISomething iObj = obj;
      
      // Outputs "42!"
      iObj.SayHi();
      

      在该示例中,内存的工作方式与我最初解释的方式相同。

      【讨论】:

        【解决方案4】:

        你的陈述

        Iinterface myDimensions = (Iinterface) myBox;
        

        没有创建任何东西,它正在将对象 myBox(必须在某处实例化)转换为 Iinterface 类型,并将 myDimesions 指向该对象。

        【讨论】:

          【解决方案5】:

          MSDN C# reference for interface 中的语言有点误导。当它说接口的实例时,它实际上意味着对接口的引用,您可以通过将对象(即类的实例)转换为接口来获得它输入。

          接口成员可以隐式实现或显式实现。当它被显式实现时,接口名称用于限定它:

          public class Box: IInterface
          {
             void IInterface.Foo() { ... }
          }
          

          这会将成员从公共类接口中隐藏起来。这样做的一个原因是版本控制,例如当类已经有一个不相关的Foo() 方法时。为了指定IInterface.Foo() 而不是Box.Foo(),唯一的方法是通过对IInterface 的引用,或者通过强制转换或者通过声明为IInterface 的变量。你可以很容易地拥有这个:

           IInterface myDimensions = myBox; //no casting needed
           myDimensions.Foo(); //calls Box.IInterface.Foo() and NOT Box.Foo()
          

          【讨论】:

            【解决方案6】:

            声明:

            interface IMy
            {
                void OhMy();
            }
            
            class Explicit : IMy
            {
                public void IMy.OhMy() { }
            }
            
            class Implicit : IMy
            {
                public void OhMy() { }
            }
            

            用法:

            Implicit i = new Implicit();
            i.OhMy(); // ok
            
            Explicit e = new Explicit();
            e.OhMy(); // it cannot be accessed through a class instance
            ((IMy)e).OhMy(); // but only through an instance of the interface
            
            IMy my = new Explicit();
            my.OhMy(); // ok
            

            【讨论】:

              【解决方案7】:

              基本上,您有一个对象 X,它由一个名为“myBox”的变量名指向。对象内存分配取决于您如何创建此对象 X。

              代码: Iinterface myDimensions = (Iinterface) myBox; 仅将您的对象 X 分配给“myDimensions”变量。由于这个变量是一个 Iinterface,你必须使用 (Iinterface) 来转换它。这是一个类型安全特性,强制你知道你正在处理什么样的对象。

              到目前为止,您至少有两个变量“myBox”和“myDimensions”指向对象 X。

              您可以创建任意数量的变量来指向该对象。如果 myBox 的变量类型未实现“Iinterface”,即使您的对象 X 支持 Iinterface,您也需要在赋值期间将“myBox”强制转换为“myDimensions”。

              顺便说一句,变量也占用内存,因为它需要存储实际实例的地址

              【讨论】:

                【解决方案8】:

                谁能给出使用这种类型实例化的任何情况。

                正如其他人所说,这并不是真正的实例化。至于它的用法,一个例子是你实际上不会看到的,因为它发生在幕后。考虑 using 语句

                using (DisposableObject foo = new DisposableObject())
                {
                   // doing things with foo
                }
                

                以及编译器如何评估该表达式并将其转换为类似

                DisposableObject foo = null;
                try 
                {
                   foo = new DisposableObject();
                   // doing things with foo
                }
                finally
                {
                   if (foo != null)
                   {
                       ((IDisposable)foo).Dispose();
                   }
                }
                

                或者考虑方法接受接口类型而不是类的其他场景。这是因为您可以有多个实现此类接口的类,但该方法只关心这些类的对象是否满足接口的约定。

                public void Dance(ICanDance dancer, bool wantTo)
                {
                     if (wantTo)
                         dancer.Dance();
                }
                

                实际的舞者可以是任何实现该接口的东西,可以是LipSynchingPopSingerStageDancerGuyThatIsNotTheFather

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-10-30
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-02-11
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-04-27
                  • 1970-01-01
                  相关资源
                  最近更新 更多