Почему возможно реализовать метод интерфейса в базовом классе? [дубликат]
этот вопрос уже есть ответ здесь:
- Почему базовый класс В C# может реализовать контракт интерфейса без наследования от него? 2 ответы
в моем проекте я нашел странную ситуацию, которая кажется полностью допустимой в C#, потому что у меня нет ошибок компиляции.
упрощенный выглядит так:using System;
using System.Collections.Generic;
namespace Test
{
interface IFoo
{
void FooMethod();
}
class A
{
public void FooMethod()
{
Console.WriteLine("implementation");
}
}
class B : A, IFoo
{
}
class Program
{
static void Main(string[] args)
{
IFoo foo = new B();
foo.FooMethod();
}
}
}
такой код компилируется. Однако, обратите внимание, что A
не IFoo
и B
не выполнять IFoo
методы. В моем случае, случайно (после рефакторинга), A
имеет метод с той же сигнатурой. Но с какой стати A
знать, как реализовать FooMethod
на IFoo
интерфейс? A
даже не знаю, что .
для меня иметь такую конструкцию опасно. Потому что каждый раз, когда я реализую какой-то интерфейс я следует проверить, если каждый метод в этом интерфейсе "мешает" с методами базового класса.
если это " чистая функция C#"? Как это называется? Я что-то упустил?
10 ответов:
для каждого члена интерфейса компилятор просто ищет явно реализация (если один), то общественные реализация (подразумевается реализация), т. е. метод на публичном API, который соответствует сигнатуре интерфейса. В этом случае
A.FooMethod()
выглядит как прекрасное соответствие для публичной реализации. ЕслиB
не был доволен этим выбором, он мог либоnew
метод, или использовать явную реализацию; последний будет предпочтительно:void IFoo.FooMethod() { /* explicit implementation */ }
ключевое слово
implements
. Ваш базовый класс, хотя он ничего не знает оIFoo
была объявлена сигнатура метода, которая реализует метод в вашем интерфейсе где-то в вашей иерархии классов.поэтому, когда вы реализуете
IFoo
в производном классе он уже имеет сигнатуру метода, реализованную в структуре класса, поэтому ее не нужно реализовывать снова.если бы у вас было это:
interface IFoo { void FooMethod(); } class A { private void FooMethod(){} } class B : A, IFoo { }
вам нужно реализовать
IFoo
в этом случае, потому чтоIFoo
структура не доступна в точке, где она реализована, и как говорит Марк. Вы можете неявно реализовать интерфейс, выполнивIFoo.FooMethod()
чтобы убедиться, что у вас есть реализация, несмотря на наличие соответствующей подписи метода, уже определенной в иерархии.
вы говорите в комментарии
насколько вероятно, что тот, кто написал реализацию
FooMethod
наA
класс, который не реализуетIFoo
на самом деле предназначены для реализацииIFoo
?Ну, это не имеет значения, что писатель
A
думал во времяA
'Ми. Это авторB
кто должен нести ответственность за то, чтоB
как наследует отA
и осуществляетIFoo
. это авторB
подумать о последствиях определенияB
.Вы тоже скажете
в моем случае случайно (после рефакторинга)
A
имеет метод с той же сигнатуройпредполагая, что эта ситуация возникла после
A
иB
были написаны. В этом случае ситуация меняется: при редактировании класса, который * наследуется от * (например,A
),это ответственность редактора за проверку влияния редактирования на все наследующие классы.
чтобы реализовать интерфейс, класс должен только (a) объявить, что он реализует этот интерфейс (например, ваш класс B), и (b) предоставить реализации для всех методов, определенных в интерфейсе, прямо или косвенно через базовый класс (например, ваш класс B).
13.4.4. в C# спецификации:
отображение интерфейса для класса или структуры C находит реализацию для каждого элемента каждого интерфейса, указанного в списке базовых классов C. реализация конкретного элемента интерфейса I. M, где I-интерфейс, в котором объявлен элемент M, определяется путем изучения каждого класса или структуры S,начиная с C и повторяя для каждого последующего базового класса С, пока не будет местонахождение:
так что кажется, что это хорошо определенное поведение, как правильная реализация
FooMethod
не найден вB
, поэтому поиск выполняется по его базовому классуA
где найден метод с соответствующей сигнатурой. Это даже явно указано в том же разделе спецификации:члены базового класса участвуют в отображении интерфейса. В Примере
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2: Class1, Interface1 { new public void G() {} }
метод F в Class1 используется в реализации Class2 Interface1.
функция называется наследование. И если вам не нравится дизайн, просто не используйте его. Многие люди не любят наследование, так что Вы тоже можете. Определение наследования состоит в том, что все члены базового класса также являются членами производного класса. Так что нет никакой ошибки компилятора. Поэтому производное реализует контракт
IFoo
обеспечивает. Это член базового класса, который выполняет это требование.прелесть в том, что вы можете реализовать интерфейс через базовый функционал (виртуальный), который может быть переопределен, если ожидается, что производное будет вести себя по-другому.
интерфейсы не наследуются, интерфейсы реализованы. Таким образом, когда вы производите класс от интерфейса, это означает
Эй интерфейс, вы найдете здесь метод, который реализует метод подпись Вы предоставили.
поскольку базовый класс имеет реализацию метода, с тем же методом signiture, определенным в интерфейсе, проблем не будет.
даже если вы пишете второй интерфейс с включением же метод подписи, он все равно будет работать.
interface IFoo2 { void FooMethod(); } class B : A, IFoo, IFoo2 { }
" но почему A должен знать, как реализовать FooMethod интерфейса IFoo? А даже не знает, что IFoo существует."
A не нужно знать о существовании интерфейса IFoo. Это не обязанность A правильно реализовать FooMethod. По-видимому, случилось реализовать метод, который имеет такую же подпись, что и метод интерфейса IFoo FooMethod.
его ответственность B реализовать FooMethod, так как он реализует интерфейс IFoo. Но так как Б уже имея метод с именем FooMethod (наследуется от A), ему не нужно реализовывать его явно. Если унаследованный метод не выполняет свою работу, B может создать новый метод и написать свою собственную реализацию.
B
реализоватьIFOO
.B
наследует отA
так это на самом деле выглядит так:class B : IFoo //Notice there is no A here. { public void FooMethod() { Console.WriteLine("implementation"); } }
и это ясно (из приведенного выше кода), что
B
осуществляетIFoo
и ничего особенного.
хотя не особенно полезно размышлять о том, почему создатели C# сделали то, что они сделали, и хотя мне не нравится эта конкретная функция, я подозреваю, что часть причины, по которой она работает, заключается в том, что нет другое хороший синтаксис, чтобы указать, что интерфейс должен быть реализован уже существующий метод базового класса. Требование, чтобы производный класс определял методы, которые не делают ничего, кроме цепочки к реализации базового класса, казалось бы уродливый.
это было сказано, Я думаю, что для C# было бы чище решить эту общую проблему (методы интерфейса, которые цепляются к другим членам, уродливы), предоставив синтаксис для явного присоединения элемента интерфейса к члену класса, чем использовать семантику автоматической привязки для обработки одной конкретной ситуации, но требуют цепочки в более общей ситуации (реализация интерфейса защищенным виртуальным методом):
защищенный виртуальный IFoo_Method(int a, int b, int с) { ... }
IFoo.Метод(int a, int b, int c) { IFoo_Method(a,b, c);}
в то время как дрожание может быть в состоянии выяснить, что вызов IFoo_Method должен быть встроен, это действительно не должно быть. Казалось бы чище объявить, что защищенный метод
IFoo_Method
следует рассматривать как реализациюIFoo.Method
.