C# - явные интерфейсы с наследованием?
Выход:
B-> Здравствуйте! из явного.
Разве это не должно быть?
A-> Привет! из явного.
Почему явное приведение (IHello) вызова IHello.Hello() из класса A не выполняется?
interface IHello
{
void Hello();
}
class A : IHello
{
public virtual void Hello()
{
Console.WriteLine("A->Hello!");
}
void IHello.Hello()
{
Console.WriteLine("A->Hello! from Explicit.");
}
}
class B : A, IHello
{
public override void Hello()
{
Console.WriteLine("B->Hello!");
}
void IHello.Hello()
{
Console.WriteLine("B->Hello! from Explicit.");
}
}
class Program
{
static void Main(string[] args)
{
A a = new B();
((IHello)a).Hello();
}
}
4 ответа
Нет, не должно.
Призыв к Hello
эквивалентно закомментированному - путь к получению IHello
не имеет значения (если только это не требует проверки во время выполнения или преобразования); тип времени компиляции просто IHello
в любом случае, и отображение интерфейса остается тем же, как и вы.
Когда интерфейс явно реализован более одного раза в иерархии типов, используется реализация в наиболее производном типе. (При вызове через интерфейс.)
Из раздела 13.4.4 спецификации C# 3.0:
При отображении интерфейса для класса или структуры C определяется реализация для каждого члена каждого интерфейса, указанного в списке базовых классов C. Определяется реализация конкретного элемента IM интерфейса, где I - интерфейс, в котором объявлен член M. исследуя каждый класс или структуру S, начиная с C и повторяя для каждого последующего базового класса C, пока не будет найдено соответствие:
- Если S содержит объявление явной реализации элемента интерфейса, которая соответствует I и M, тогда этот элемент является реализацией IM
- В противном случае, если S содержит объявление нестатического открытого члена, который соответствует M, тогда этот член является реализацией IM. Если более одного члена соответствует, то не указано, какой элемент является реализацией IM. Эта ситуация может возникнуть, только если S это составной тип, в котором два члена, объявленные в универсальном типе, имеют разные сигнатуры, но аргументы типа делают их сигнатуры идентичными.
(А) ничего не делает. Ссылка уже объявлена, так что приведение к A не будет иметь никакого эффекта.
Несмотря на то, что ваша ссылка объявлена как A, объект, на который она ссылается, имеет тип B. Если вы приведете этот объект к IHello, вызов Hello() вызовет явную реализацию объекта B для Hello.
Вывод точно такой, как ожидалось.
Нет, не должно.
Когда вы вызываете виртуальный метод, не имеет значения, какой тип ссылки. Вызываемый метод определяется фактическим типом объекта, а не типом ссылки.
Как вы создаете экземпляр класса B
, фактический тип объекта B
, Причина, по которой он печатает "This is class A."
является то, что вы не изменили ToString
метод в классе B
Вы затеняли его, используя new
ключевое слово. Следовательно B
класс имеет два ToString
методы, унаследованные от класса A
и тот, который скрывает это. Если вы используете A
ссылка для вызова ToString
метод, унаследованный метод вызывается, но если бы вы использовали B
ссылка на его вызов, будет вызван метод теневого копирования, распечатка "This is class B."
,
Также, если вы переопределите ToString
метод в классе B
вместо того, чтобы затенять его, он распечатал бы "This is class B."
независимо от типа ссылки.
Нет, когда вы создаете новый метод (ToString), он не является виртуальным. Новый просто означает, что вы не возражаете против того, чтобы он "скрывал" версию в базовом классе - поэтому, если вы вызываете его со ссылкой, приведенной к определенному типу (A), он выполняет метод в классе A, независимо от того, фактический тип объекта, который вы вызываете. (т.е. вы вызвали A.ToString(), поэтому он выполнил A.ToString())
Когда вы создаете виртуальный метод, то независимо от того, к какому типу вы приводите ссылку, используется реализация из фактического типа объекта (т. Е. Вы создали B, поэтому при вызове (каким бы ни был объект). Здравствуйте, это называется Б. Привет)
Принципиальное отличие состоит в том, что один вызов является виртуальным, а другой - нет.