【发布时间】:2010-09-16 18:10:08
【问题描述】:
我是 C#.net 的新手,很惊讶地知道可以像这样创建接口的实例
Iinterface myDimensions = (Iinterface) myBox;
如何为这种类型的语句分配内存?内存是在堆上分配的吗?
谁能给出使用这种类型实例化的任何情况。
实现接口的类可以显式实现该接口的成员。当成员显式实现时,不能通过类实例访问,只能通过接口实例访问。
为什么要在语言中强制执行这样的约束?
谢谢,
【问题讨论】:
我是 C#.net 的新手,很惊讶地知道可以像这样创建接口的实例
Iinterface myDimensions = (Iinterface) myBox;
如何为这种类型的语句分配内存?内存是在堆上分配的吗?
谁能给出使用这种类型实例化的任何情况。
实现接口的类可以显式实现该接口的成员。当成员显式实现时,不能通过类实例访问,只能通过接口实例访问。
为什么要在语言中强制执行这样的约束?
谢谢,
【问题讨论】:
在回答您的第一个问题之前,我注意到您有两个问题。将来,当您有两个问题时,请考虑在 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 方法有不同的实现?如果您想要两个不同的主体,则必须对其中一个或两个使用显式实现。
【讨论】:
这不是实例化——它是类型转换。该实例是原始对象,即myBox。至于内存,是的,为引用分配了内存——它是在堆上还是在堆栈上完全取决于上下文。在您的示例中(看起来像是在函数中),我猜是堆栈。
关于显式实现:语言特性允许单个类实现两个或多个接口,其中包含一个或多个具有完全相同签名的成员。例如:
interface A
{
void Foo();
}
interface B
{
void Foo();
}
class C : A, B
{
void A.Foo()
{
}
void B.Foo()
{
}
}
如果没有这个特性,编译器就不清楚C.Foo 实现了哪个接口成员。当然,需要注意的是,调用者不能简单地调用C.Foo,因为调用哪个方法也不是很明显;因此,C 类型的对象必须首先转换为 A 或 B,以明确程序员的愚蠢意图。
【讨论】:
myBox 是否是值类型,myDimensions 都会被视为引用类型(因此是堆分配的)。因此,如果myBox 在堆栈上,myDimensions 将是它在堆上的盒装副本。
myBox是值类型,那么装箱值所需的内存将分配在堆上。
var c = new C(); c.Foo();,不清楚你想调用哪个Foo——所以你必须进行类型转换。隐藏(几乎)是显式实现的全部要点:您不需要显式实现成员;它仅在这些情况下才真正有用。 (或者类似的情况,C : A 和 C 已经有了Foo()。)
你所拥有的是一个演员,而不是一个实例化。在这种情况下,必须在其他地方创建对象:
// 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();
在该示例中,内存的工作方式与我最初解释的方式相同。
【讨论】:
你的陈述
Iinterface myDimensions = (Iinterface) myBox;
没有创建任何东西,它正在将对象 myBox(必须在某处实例化)转换为 Iinterface 类型,并将 myDimesions 指向该对象。
【讨论】:
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()
【讨论】:
声明:
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
【讨论】:
基本上,您有一个对象 X,它由一个名为“myBox”的变量名指向。对象内存分配取决于您如何创建此对象 X。
代码: Iinterface myDimensions = (Iinterface) myBox; 仅将您的对象 X 分配给“myDimensions”变量。由于这个变量是一个 Iinterface,你必须使用 (Iinterface) 来转换它。这是一个类型安全特性,强制你知道你正在处理什么样的对象。
到目前为止,您至少有两个变量“myBox”和“myDimensions”指向对象 X。
您可以创建任意数量的变量来指向该对象。如果 myBox 的变量类型未实现“Iinterface”,即使您的对象 X 支持 Iinterface,您也需要在赋值期间将“myBox”强制转换为“myDimensions”。
顺便说一句,变量也占用内存,因为它需要存储实际实例的地址
【讨论】:
谁能给出使用这种类型实例化的任何情况。
正如其他人所说,这并不是真正的实例化。至于它的用法,一个例子是你实际上不会看到的,因为它发生在幕后。考虑 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();
}
实际的舞者可以是任何实现该接口的东西,可以是LipSynchingPopSinger、StageDancer 或GuyThatIsNotTheFather。
【讨论】: