Подтвердить что ты не робот

Зачем бросать в интерфейс?

В Jesse Liberty Programming С# (стр .142) он приводит пример, в котором он передает объект в интерфейс.

 interface IStorable
 {
    ...
 }

 public class Document : IStorable
 {
    ...
 }

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

В чем смысл этого, особенно если класс объекта все равно реализует inteface?

EDIT1: Чтобы уточнить, меня интересует причина для приведения (если есть), а не причина для реализации интерфейсов. Кроме того, книга является его первым выпуском 2001 года (на основе С# 1, поэтому пример не может быть родным для более поздних версий С#).

EDIT2: я добавил некоторый контекст в код

4b9b3361

Ответ 1

Есть только одна причина, когда вам действительно нужна акция: Когда doc имеет базовый тип фактического объекта, который реализует 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();
}

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

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

EDIT: Как мы уже видели в ответах, здесь есть три возможных вопроса:

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

Ответ 6

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

Фактически "casting" (с использованием синтаксиса (T)) не имеет никакого смысла, поскольку С# обрабатывает upcasts (cast to parent type) автоматически (в отличие от F #, например).

Ответ 7

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

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

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

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

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

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

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

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

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

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

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

Ответ 8

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

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

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

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. Затем я смогу использовать интерфейс, чтобы узнать, что еще они могут иметь.

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

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

Ответ 9

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

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

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

Ответ 10

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

Ответ 11

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

Подробнее см. в следующей статье: Interfaces