Значение интерфейсов C # - PullRequest
       24

Значение интерфейсов C #

10 голосов
/ 08 февраля 2010

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

Я написал небольшую программу. Я определил интерфейс Itest.Class(Manager) реализовал Interface.Another class(Employee) не реализовал интерфейс. Но определен тот же метод (DoSomething()) в интерфейсе в классе (Employee). Я могу вызвать метод из объекта класса. Тогда почему я должен пойти и реализовать интерфейс. Я могу напрямую реализовать метод в классе и вызвать метод. Почему я должен пойти на дополнительный шаг реализации метода в интерфейсе, а затем наследовать интерфейс по классу. Я знаю, что интерфейс поддерживает множественное наследование, но в этом примере я не использую множественное наследование.

Спасибо за любые идеи или вклад.

public interface Itest
{
    void DoSomething();
}

public class Manager:Itest
{
    public void DoSomething()
    {
        Console.WriteLine("test....");
    }

}
class Employee
{
    public void DoSomething()
    {
        Console.WriteLine("test....");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Manager m = new Manager();
        m.DoSomething();
        Employee e = new Employee();
        e.DoSomething();
        Console.ReadLine();
    }
}

Ответы [ 12 ]

15 голосов
/ 08 февраля 2010

Интерфейсы позволяют сортировать множественное наследование. В вашем примере это позволит вам поместить экземпляр Employee или Manager в ту же переменную, а затем вызвать DoSomething для этой переменной, при этом вызов метода будет отправлен в экземпляр, на который в данный момент ссылается эта переменная. Например:

public interface IEmployee {
    void DoSomething();
}
// assume Manager and Employee both implement IEmployee

IEmployee ie = new Manager();
ie.DoSomething();    // calls Manager.DoSomething()
ie = new Employee();
ie.DoSomething();    // calls Employee.DoSomething()

Если вы не используете интерфейсы, вам нужно будет сделать:

object o;

// assign Manager or Employee (or something else!) to o

if (o is Manager)
    ((Manager)o).DoSomething();
else if (o is Employee)
    ((Employee)o).DoSomething();

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

Одним из примеров, где я сейчас использую интерфейсы, является определение объектной модели - у меня есть интерфейсы для различных свойств (IHasStorage, IHasPrivileges, IHasCheezburger), затем классы, представляющие конкретные объекты, реализуют то, что и тем не менее многие интерфейсы подходят для свойств этого класса

10 голосов
/ 08 февраля 2010

Интерфейсы используются для абстракции (абстрактные классы также используются для этого, но обычно содержат некоторую реализацию, предназначенную для повторного использования).

В C # они допускают использование множественного наследования, то есть вы можете реализовать многоразные интерфейсы в одном классе.

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

Например:

IAnimal может быть реализовано с помощью Cat и Dog.В другом коде вы хотите вызвать метод Talk, который объявлен в интерфейсе.Ваш код не должен заботиться о том, является ли он Cat объектом или Dog объектом.Вы можете добавить Duck или Human и не изменять этот фрагмент кода.

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

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

4 голосов
/ 08 февраля 2010

Реализация наследования моделирует отношение «Я ВИДИМО», в то время как наследование интерфейса моделирует отношение «МОЖЕТ ПОВЕДИТЬСЯ КАК». Не случайно многие имена интерфейсов BCL оканчиваются на «-able», обозначая способность что-то делать. Чтобы продемонстрировать, изобразите следующий код:

class Person
{
  public string Name{get;set;}
  public void Walk() {/*code elided*/}
}

class Employee : Person
{
  public int Id{get;private set;}
}

Ясно, что Сотрудник - это своего рода "Человек". Все сотрудники являются людьми, поэтому у них всех есть Имя и они могут ходить (). Обязательно делайте абстракцию «Человек» или «Сотрудник», это не меняет того факта, что все сотрудники являются людьми.

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

Давайте подумаем о некоторых вещах, которые являются транспортными средствами. Автомобиль, конечно, но как насчет человека? Они могут двигаться и останавливаться (и когда я даю своим племянникам и племянникам свиные спины, я тоже несу пассажиров!) Я вечно слоняюсь по офису. Я тоже заядлый моряк и использую ветер для ускорения и замедления моего транспортного средства.

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

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

interface IVehicle
{
  void Move();
  void Stop();
}

class Toy{}

class PogoStick : Toy, IVehicle
{
  public void Move(){ /* boing boing */}
  public void Stop(){ /* fall over */}
}

class Car: IVehicle
{
  public void Move(){ /* vroom vroom */}
  public void Stop(){ /* <screeeech!> */}
}

class Person : IVehicle
{
  public string Name{get;set;}
  public void Walk() {/*code elided*/}
  void IVehicle.Move() { Walk(); }
  void IVehicle.Stop() { /*whatever!*/}
}

class Program
{
  static void Main()
  {
    IVehicle[] vehicles = new IVehicle[3];
    vehicles[0] = new PogoStick();
    vehicles[1] = new Car();
    vehicles[2] = new Employee(); //implements IVehicle because it IS A KIND OF Person

    vehicles.ForEach(v => v.Move());

    //it's worth pointing out that
    vehicles[2].Stop();
    //works fine, but
    Person p = new Person();
    p.Move();
    //won't, as I explicitly implemented the interface, meaning I can only get at the
    //methods via a reference to the interface, not to the implementing class.
  }
}

Чтобы использовать пример из самого .NET, что общего у строки со списком? Не много, за исключением того, что я могу "предвидеть" их обоих:

class Demo
{
  static void Main()
  {
    string s = "Hello!";
    List<Employee> payroll = new List<Employee>();

    for each (var item in s)
    {
      Console.WriteLine(item);
    }

    for each (var item in payroll)
    {
      Console.WriteLine(item);
    }
}

Общий базовый класс для string и List - это object, но не все объекты «для каждого», поэтому должно быть что-то еще. А именно, они оба реализуют интерфейс IEnumerable (это возможно!)

2 голосов
/ 08 февраля 2010

Когда вы добавляете интерфейс в Employee, два класса становятся совместимыми через этот интерфейс:

public class Manager:Itest { ... }

class Employee:Itest { ... }

static void ShowSomething(Itest test)
{
   test.DoSomething();
}

static void Main(string[] args)
{

    Manager m = new Manager();            
    Employee e = new Employee();

    ShowSomething(m);
    ShowSomething(e);
}

Посмотрите, как BCL использует интерфейс, чтобы начать искать IDisposable, ISerializable, IComprable и IEnumerable

1 голос
/ 08 февраля 2010

Если у вас слишком много классов, которые управляются из класса Interface, вы гарантируете, что все они реализуют Метод.

Тогда, если ваш интерфейс изменится (например:

       public Interface IAnimal
            {
                public void Talk();
                public void Eat();
            }

и затем вы добавляете другой метод

       public Interface IAnimal
            {
                public void Talk();
                public void Sleep();
                public void Eat();
            }

тогда вы можете убедиться, что все они будут реализовывать метод Sleep (). если у вас слишком много классов, которые являются деликатесами из IAnimal Inteface, тогда вам нужно реализовать Sleep () для всех них. это поможет вам максимально расширить ваши производные классы

1 голос
/ 08 февраля 2010

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

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

interface IGraphicsRenderer
{
     Render(List<Triangle> triangles);
}

Тогда у вас может быть 2 типа движков рендеринга, Direct3D или OpenGL

class Direct3DRenderer : IGraphicsRenderer
{
   public void Render(List<Triangle> triangles);
}

class OpenGLRenderer: IGraphicsRenderer
{
   public void Render(List<Triangle> triangles);
}

Чтобы показать, что это полезно, у вас может быть что-то вроде

IGraphicsRenderer renderer = new Direct3DRenderer();
renderer.Render(foo);

Чтобы изменить средство визуализации, все, что вам нужно сделать, это изменить инициализацию.

IGraphicsRenderer renderer = new OpenGLRenderer();
1 голос
/ 08 февраля 2010

интерфейсы полезны в немного более сложных сценариях, например

(1) Вам нужно множественное наследование (в c # вы не можете наследовать от 2 классов), например, у вас есть интерфейсы IProduct, IDisposable. Не каждый продукт должен быть утилизирован, поэтому нет смысла применять его на всех продуктах и ​​т. Д.

(2) Когда вы используете инъекцию зависимостей (инверсию управления) и фреймворк (например, RhinoMocks) для модульного тестирования - тогда вам придется работать с интерфейсами, иначе ваш фреймворк не будет работать.

1 голос
/ 08 февраля 2010

Приведена очень хорошая аналогия для интерфейсов Мэтью Кокран

«Это значительно упрощает« мир кодов »для навигации. Представьте, что вместо того, чтобы научиться водить машину, а затем управлять любым автомобилем, нам нужно научиться водить каждый экземпляр каждой машины. мы входим. Это было бы действительно неэффективно, если бы после изучения того, как водить Ford Pinto, нам пришлось бы начинать все сначала, чтобы выяснить Mustang. Гораздо более эффективный способ - разобраться с интерфейсом машины: рулевое колесо поворотники, педаль газа и тормоз. Таким образом, независимо от того, что реализовано на бэкэнде интерфейса, нам все равно, потому что в конце концов он подписывается на базовый автомобильный контракт, и именно так мы будем с ним справляться ( через интерфейс) .2

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

Рассмотрим этот пример:

У вас есть класс, способный воспроизводить медиафайлы (mp3). Вы отдаете этот класс своему другу, который пытается воспроизводить файлы типа MPEG. Он не сможет сделать это без значительных изменений в вашем классе.

public class MusicPlayer 
    {
       void Play(Mp3 _mp3File){}
    }

учтите это

Вместо передачи типа mp3-файла методу воспроизведения, что если вы передадите этот метод, получаемый из интерфейса Type MediaType.

    public interface MediaType { }

    public class Mp3 : MediaType
    { }

    public class MPEG : MediaType
    { }

и класс:

    public class MusicPlayer 
    {
       void Play(MediaType _mediaFile){}
    }

В этом сценарии вы можете получить другой тип MediaFile и MediaType, например MPEG, и передать его в метод Play, и он с радостью примет его и воспроизведет для вас (при условии логики).

   public class TestPlayers
    {
        public void PlayMedia()
        {
            MusicPlayer musicPlayer = new MusicPlayer();
            musicPlayer.Play(new Mp3());
            musicPlayer.Play(new MPEG());
        }       
    }

Надеюсь, это поможет

1 голос
/ 08 февраля 2010

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

0 голосов
/ 08 февраля 2010

Интерфейсы для дизайна. Реальная реализация следует тому, что определено в проекте (интерфейс чтения). Концепция интерфейса дает гибкость при проектировании системы, не вдаваясь в детали реализации или сохраняя расширяемость проекта путем реализации интерфейсов в будущем. Это также называется абстракцией.

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

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

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