Экземпляры интерфейса

Я новичок в C#.net и был удивлен, узнав, что экземпляры интерфейса могут быть созданы как

Iinterface myDimensions = (Iinterface) myBox;

Как выделяется память для этого типа оператора? Выделена ли память в куче?

может ли кто-нибудь дать любую ситуацию, в которой используются эти типы экземпляров.

класс, реализующий интерфейс, может явно реализовать член этого интерфейса. Когда элемент явно реализован, к нему нельзя получить доступ через экземпляр класса, но только через экземпляр интерфейса.

почему такое ограничение применяется в языке?

спасибо,

8 ответов


прежде чем ответить на ваш первый вопрос, замечу, что у вас тут два вопроса. В будущем подумайте о том, чтобы попросить два отдельные вопросы на переполнение стека, когда у вас есть два вопроса. Как вы могли заметить, когда вы задаете два вопроса в одном, часто второй вопрос игнорируется почти всеми.

Я был удивлен, узнав, что экземпляры интерфейса могут быть созданы как

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(); }

Теперь, как вы делаете класс C, который реализует IFoo и IBar, но имеет разные реализации для двух методов 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 чтобы прояснить намерение программиста.


то, что у вас есть, - это гипс, а не экземпляр. В этом случае объект уже должен быть создан где-то еще:

// 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, относится к явным реализациям интерфейса. Это может немного запутать. Самый простой пример:

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# для 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" к "myDimensions" во время назначения, если тип переменной myBox не реализует "Iinterface", даже если ваш объект X поддерживает Iinterface.

в качестве примечания, переменные также занимают память, потому что ей нужно сохранить адрес фактического экземпляра


может ли кто-нибудь дать любую ситуацию, в которой используются эти типы экземпляров.

как говорили другие, это на самом деле не экземпляр. Что касается его использования, одним из примеров является тот, который вы на самом деле не посмотреть, потому что это происходит под одеялом. Рассмотрим оператор 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.