Вопрос

В книге Джесси Либерти «Программирование на C#» (стр. 142) он приводит пример, в котором он приводит объект к интерфейсу.

 interface IStorable
 {
    ...
 }

 public class Document : IStorable
 {
    ...
 }

 ...
 IStorable isDoc = (IStorable) doc;  
 ...

Какой в ​​этом смысл, особенно если класс объекта все равно реализует интерфейс?

РЕДАКТИРОВАТЬ1:Чтобы внести ясность, меня интересует причина актерского состава (если есть), нет причина реализации интерфейсов.Кроме того, эта книга является его первым изданием 2001 года (основана на C#1, поэтому пример может быть неприменим для более поздних версий C#).

РЕДАКТИРОВАТЬ2:Я добавил некоторый контекст в код

Это было полезно?

Решение

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

public class DocBase
{
  public virtual void DoSomething()
  {

  }
}

public class Document : DocBase, IStorable
{
  public override void DoSomething()
  {
    // Some implementation
    base.DoSomething();
  }

  #region IStorable Members

  public void Store()
  {
    // Implement this one aswell..
    throw new NotImplementedException();
  }

  #endregion
}

public class Program
{
  static void Main()
  {
    DocBase doc = new Document();
    // Now you will need a cast to reach IStorable members
    IStorable storable = (IStorable)doc;
  }
}

public interface IStorable
{
  void Store();
}

Другие советы

Потому что вы хотите ограничиться только методами, предоставляемыми интерфейсом. Если вы используете класс, вы рискуете вызвать метод (непреднамеренно), который не является частью интерфейса.

Если объект реализует интерфейс явно ( public void IStorable.StoreThis (...) ), то приведение - это самый простой способ на самом деле достичь членов интерфейса.

Я не уверен, в каком контексте пример был приведен в книге. Но, как правило, вы можете вводить объект для интерфейса для достижения множественного наследования. Я привел пример ниже.

public interface IFoo
{
     void Display();
}
public interface IBar
{
     void Display();
}

public class MyClass : IFoo, IBar
{
    void IBar.Display()
    {
        Console.WriteLine("IBar implementation");
    }
    void IFoo.Display()
    {
        Console.WriteLine("IFoo implementation");
    }
}

public static void Main()
{
    MyClass c = new MyClass();
    IBar b = c as IBar;
    IFoo f = c as IFoo;
    b.Display();
    f.Display();
    Console.ReadLine();
}

Это отобразит

  

Внедрение IBar
  Реализация IFoo

Без контекста довольно сложно сказать.Если переменная doc объявлен как тип, реализующий интерфейс, то приведение является избыточным.

Какую версию книги вы читаете?Если это «Программирование на C# 3.0», я посмотрю сегодня вечером, когда буду дома.

РЕДАКТИРОВАТЬ:Как мы уже видели в ответах, здесь есть три потенциальных вопроса:

  • Зачем использовать утверждение, показанное в вопросе?(Отвечать:тебе не обязательно, если doc имеет соответствующий тип времени компиляции)
  • Почему вообще уместно явное приведение к реализованному интерфейсу или базовому классу?(Отвечать:явная реализация интерфейса, как показано в другом ответе, а также ради выбора менее конкретной перегрузки при передаче приведения значения в качестве аргумента.)
  • Зачем вообще использовать интерфейс?(Отвечать:работа с типом интерфейса означает, что вы менее восприимчивы к изменениям конкретного типа в дальнейшем.)

Объект doc может относиться к типу, который явно реализует члены IStorable , не добавляя их в первичный интерфейс классов (т. е. они могут быть вызваны только через интерфейс).

На самом деле "кастинг" (использование синтаксиса (T)) не имеет никакого смысла, поскольку C # автоматически обрабатывает апкасты (приведение к родительскому типу) (в отличие, например, от F #).

Здесь есть много хороших ответов, но я не думаю, что они отвечают, ПОЧЕМУ вы на самом деле ХОТИТЕ использовать максимально ограниченный интерфейс.

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

Допустим, вы хотите кнопку и размещаете ее на экране. Вы получаете кнопку, переданную или из другой функции, например:

Button x=otherObject.getVisibleThingy();
frame.add(x);

Вы знаете, что VisibleThingy - это кнопка, она возвращает кнопку, так что здесь все круто (не требуется приведение).

Теперь допустим, что вы реорганизовали VisibleThingy, чтобы вместо него вернуть кнопку переключения. Теперь вам нужно провести рефакторинг вашего метода, потому что вы слишком много знали о реализации.

Поскольку вам НУЖНЫ только методы в Component (родительские элементы для кнопки и Toggle, которые могли бы быть интерфейсом - то же самое для наших целей), если вы написали эту первую строку следующим образом:

Component x=(Component)otherObject.getVisibleThingy();

Тебе не пришлось бы что-либо реорганизовывать - это бы просто сработало.

Это очень простой случай, но он может быть гораздо более сложным.

Итак, я полагаю, что в итоге интерфейс - это особый способ " просмотра " ваш объект - как если бы вы смотрели на него через фильтр ... вы можете видеть только некоторые его части. Если вы можете достаточно ограничить свой обзор, объект может "Morph" за вашим конкретным взглядом и никак не влияющим на ваш нынешний мир - очень мощная уловка абстракции.

Лучшая причина, по которой вы будете приводить к интерфейсам, - это если вы пишете код для объектов, и вы не знаете, что это за конкретный тип, и не хотите.

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

Вот простой пример:

public interface IText
{
   string Text { get; }
}

public interface ISuperDooper
{
   string WhyAmISuperDooper { get; }
}

public class Control
{
   public int ID { get; set; }
}

public class TextControl : Control, IText
{
   public string Text { get; set; }
}

public class AnotherTextControl : Control, IText
{
   public string Text { get; set; }
}

public class SuperDooperControl : Control, ISuperDooper
{
   public string WhyAmISuperDooper { get; set; }
}

public class TestProgram
{
   static void Main(string[] args)
   {
      List<Control> controls = new List<Control>
               {
                   new TextControl
                       {
                           ID = 1, 
                           Text = "I'm a text control"
                       },
                   new AnotherTextControl
                       {
                           ID = 2, 
                           Text = "I'm another text control"
                       },
                   new SuperDooperControl
                       {
                           ID = 3, 
                           WhyAmISuperDooper = "Just Because"
                       }
               };

       DoSomething(controls);
   }

   static void DoSomething(List<Control> controls)
   {
      foreach(Control control in controls)
      {
         // write out the ID of the control
         Console.WriteLine("ID: {0}", control.ID);

         // if this control is a Text control, get the text value from it.
         if (control is IText)
            Console.WriteLine("Text: {0}", ((IText)control).Text);

         // if this control is a SuperDooperControl control, get why
         if (control is ISuperDooper)
            Console.WriteLine("Text: {0}", 
                ((ISuperDooper)control).WhyAmISuperDooper);
      }
   }
}

запуск этой маленькой программы даст вам следующий вывод:

  

ID: 1

     

Текст: я текстовый элемент управления

     

ID: 2

     

Текст: я другой текстовый элемент управления

     

ID: 3

     

Текст: только потому, что

Обратите внимание, что мне не нужно было писать код в методе DoSomething, который требовал, чтобы я знал что-либо обо всех объектах, над которыми я работал, являющихся конкретными типами объектов. Единственное, что я знаю, это то, что я работаю над объектами, которые являются, по крайней мере, экземпляром класса Control. Затем я могу использовать интерфейс, чтобы узнать, что еще они могут иметь.

Существует миллион различных причин, по которым вы бы выбрали этот подход с интерфейсами для ваших объектов, но он дает вам свободный способ доступа к вашим объектам без необходимости точно знать, что это такое.

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

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

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

" документ " не относится к типу "IStorable" поэтому для начинающих было бы странно видеть, что он назначается для isDoc. При явном приведении автор (книги и кода) говорит, что документ может быть приведен к объекту IStorable, но он НЕ ЖЕ, как объект IStorable.

Суть в том, что объект (откуда вы его взяли?) может не реализовывать интерфейс, и в этом случае выдается исключение, которое может быть перехвачено и обработано. Конечно, вы можете использовать " is " оператор для проверки, а " как " оператор для приведения вместо приведения в стиле C.

Чтобы максимально разделить фрагменты кода ...

См. следующую статью: Интерфейсы

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top