Реализация наследования моделирует отношение «Я ВИДИМО», в то время как наследование интерфейса моделирует отношение «МОЖЕТ ПОВЕДИТЬСЯ КАК». Не случайно многие имена интерфейсов 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 (это возможно!)