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

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

6 голосов
/ 16 сентября 2010

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

Iinterface myDimensions = (Iinterface) myBox;

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

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

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

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

Спасибо

Ответы [ 8 ]

13 голосов
/ 16 сентября 2010

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

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

Iinterface myDimensions = (Iinterface) myBox; 

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

Как уже отмечали другие, это не обязательно создание экземпляра типа, который реализует интерфейс.Кажется, что все забыли в спешке сказать вам, что эталонные преобразования не выделяют память , это то, что бокс-преобразования действительно выделяют память .Если myBox имеет тип struct, то это выделит память в куче для «обертки» и создаст копию значения в обертке.Затем оболочка реализует интерфейс.

Переходя ко второму вопросу:

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

Цель явной реализации интерфейса состоит в том, чтобы позволить классу реализовать определенный интерфейс, не требуя, чтобы эти методы отображались местамиони не нужны.Например, предположим, что у вас есть:

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?Вы должны использовать явную реализацию для одного или обоих из них, если вам нужны два разных тела.

10 голосов
/ 16 сентября 2010

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

7 голосов
/ 16 сентября 2010

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

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

В этом примере память работает так же, как я сначала объяснил.*

2 голосов
/ 16 сентября 2010

Декларация:

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
2 голосов
/ 16 сентября 2010

Язык в справочнике 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()
2 голосов
/ 16 сентября 2010

ваше утверждение

Iinterface myDimensions = (Iinterface) myBox;

ничего не создает, оно приводит объект myBox (который должен быть где-то создан) к типу Iinterface и указывает myDimesions на этот объект.

0 голосов
/ 16 сентября 2010

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

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

0 голосов
/ 16 сентября 2010

В основном у вас есть объект X, на который указывает переменная с именем myBox.Распределение памяти объекта зависит от того, как вы создаете этот объект X.

Код: Iinterface myDimensions = (Iinterface) myBox;присваивайте свой объект X только переменной myDimensions.Поскольку эта переменная является Iinterface, вы должны использовать (Iinterface) для ее приведения.Это функция безопасности типов, которая заставляет вас знать, с каким объектом вы имеете дело.

К настоящему времени у вас есть как минимум две переменные "myBox" и "myDimensions", которые указывают на объект X.

Вы можете создать столько переменных, сколько хотите указать на этот объект.Вам нужно будет преобразовать «myBox» в «myDimensions» во время присваивания, если тип переменной myBox не реализует «Iinterface», даже если ваш объект X поддерживает Iinterface.памяти, потому что он должен хранить адрес самого экземпляра

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...