Почему класс С# наследует от одного интерфейса как неявно, так и явно?
Сегодня я обнаружил, что один класс С# может наследовать один интерфейс как неявным, так и явным образом. Это меня удивляет. Если С# работает таким образом, то один экземпляр может вести себя по-разному, если ссылается по-разному.
interface IFoo
{
void DoSomething();
}
class Foo : IFoo
{
#region IFoo Members
public void DoSomething()
{
Console.WriteLine("do something implicitly");
}
#endregion
#region IFoo Members
void IFoo.DoSomething()
{
Console.WriteLine("do something explicitly");
}
#endregion
}
Foo f = new Foo();
f.DoSomething();
((IFoo)f).DoSomething();
Выше кода запускается и выводится
do something implicitly
do something explicitly
Я считаю, что этот дизайн С# делает непоследовательность поведения. Возможно, что один класс С# может наследовать от одного интерфейса неявным или эксплицитным способом, но не тем и другим.
Есть ли причина, почему С# сконструирован таким образом?
Ответы
Ответ 1
В вашем примере не реализует IFoo как неявно, так и явно. Вы явно реализуете IFoo.DoSometing(). У вас есть новый метод в вашем классе под названием DoSomething(). Это не имеет никакого отношения к IFoo.DoSomething, за исключением того, что у него одинаковое имя и параметры.
Ответ 2
Каждый класс, реализующий интерфейс, имеет сопоставление между этими членами класса и членами интерфейса. Если класс явно реализует элемент интерфейса, то явная реализация будет отображать всегда в интерфейс. Если не существует явной реализации, ожидается неявная реализация, и она будет отображена на интерфейс.
Если класс имеет одно и то же имя участника и связанные с ним типы в качестве интерфейса , но, он также явно реализует соответствующий элемент для интерфейса, тогда реализация класса "неявное" не рассматривается как реализация интерфейс вообще (если только явная реализация не вызывает его).
В дополнение к различным значениям в каждом случае, когда класс реализует несколько интерфейсов с одинаковыми именами/типами элементов, даже с одним интерфейсом, сам класс считается неявным интерфейсом, который может иметь одинаковые элементы/типы, такие как единственный интерфейс, но все же означает что-то другое.
Ответ 3
Это делает его более гибким, когда возникают столкновения. В частности, посмотрите IEnumerator
и IEnumerator<T>
- они оба имеют свойство Current
, но разных типов. Вы должны использовать явную реализацию интерфейса для реализации обоих (и общая форма расширяет не-общую форму).
Ответ 4
Множественное наследование:
Что делать, если вы извлекаете из двух интерфейсов, определяющих один и тот же метод для разных целей?
interface IMoveable
{
public void Act();
}
interface IRollable
{
public void Act();
}
class Thing : IMoveable, IRollable
{
//TODO Roll/Move code here
void IRollable.Act()
{
Roll();
}
void IMoveable.Act()
{
Move();
}
}
Ответ 5
Ребята, спасибо за ваши ответы.
Оказывается, что "класс С# может наследовать один интерфейс как неявным, так и явным образом в одно и то же время" на самом деле является иллюзией. На самом деле один класс может наследовать один интерфейс за один раз.
В исходном вопросе метод "DoSomething" кажется "неявно реализуемым" интерфейсом IFoo (метод фактически генерируется VS2008), но на самом деле это НЕ. С явной реализацией интерфейса IFoo метод "DoSomething" превращается в обычный метод, который не имеет ничего общего с IFoo, кроме одной и той же сигнатуры.
Я все еще считаю, что это сложный дизайн С#, и его легко использовать ошибочно. Скажем, у меня есть код вроде этого
Foo f = new Foo();
f.DoSomething();
Теперь я хочу реорганизовать его ниже кода. Кажется, все в порядке, но результат выполнения отличается.
Action<IFoo> func = foo => foo.DoSomething();
func(f);