Почему так?
От: Аноним  
Дата: 13.10.09 08:27
Оценка:
Привет всем.
Интересует вопрос по следующему коду:

    class Program
    {
        public interface II
        {
            void Foo();
        }

        public class A : II
        {
            public void Foo()
            {
                Console.WriteLine("A.Foo()");
            }
        }

        public class B : A //, II
        {
            public void Foo()
            {
                Console.WriteLine("B.Foo()");
            }
        }

        static void Main(string[] args)
        {
            B b = new B();
            (b as II).Foo();
            Console.ReadLine();
        }
    }


Есть некий интерфейс II. Есть класс (А), его реализующий, и класс, унаследованный от него (В).
В теле метода Main создается экземпляр класса В, приводится к интерфейсу и вызывается метод. На выходе получаем строку "A.Foo()". Если раскомментировать указание явного наследования класса В от интерфейса вызовется именно его метод, т.е. на экран будет выведено "В.Foo()".
Почему так?
Я имею в виду, почему не наследуется "контракт" с интерфейсом в классе В и компилятор не видит этой реализации.

PS.: виртуальные методы не предлагать (знаю, что с ними код более "красивый" и тогда все будет работать без возникновения доп-ных вопросов). Интересует сама теория без использования виртуальности при использовании интерфейсов.
Re: Почему так?
От: OrSol  
Дата: 13.10.09 08:47
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Привет всем.

А>Почему так?

Интерфейс II реализует класс A, и получается, что метод класса B уже не имеет отношения к интерфейсу II.
Вобщем спасет слово new перед B.Foo
Re: Почему так?
От: _FRED_ Черногория
Дата: 13.10.09 09:05
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Интересует вопрос по следующему коду:

А>    class Program
А>    {
А>        public interface II
А>        {
А>            void Foo();
А>        }

А>        public class A : II
А>        {
А>            public void Foo()
А>            {
А>                Console.WriteLine("A.Foo()");
А>            }
А>        }

А>        public class B : A //, II
А>        {
А>            public void Foo()
А>            {
А>                Console.WriteLine("B.Foo()");
А>            }
А>        }

А>        static void Main(string[] args)
А>        {
А>            B b = new B();
А>            (b as II).Foo();
А>            Console.ReadLine();
А>        }
А>    }


А>Есть некий интерфейс II. Есть класс (А), его реализующий, и класс, унаследованный от него (В).

А>В теле метода Main создается экземпляр класса В, приводится к интерфейсу и вызывается метод. На выходе получаем строку "A.Foo()". Если раскомментировать указание явного наследования класса В от интерфейса вызовется именно его метод, т.е. на экран будет выведено "В.Foo()".
А>Почему так?
А>Я имею в виду, почему не наследуется "контракт" с интерфейсом в классе В и компилятор не видит этой реализации.
А>PS.: виртуальные методы не предлагать (знаю, что с ними код более "красивый" и тогда все будет работать без возникновения доп-ных вопросов). Интересует сама теория без использования виртуальности при использовании интерфейсов.

Потому что надо внимательно читать сообщения компилятора:

Program.cs(19,15): warning CS0108: 'Program.B.Foo()' hides inherited member 'Program.A.Foo()'. Use the new keyword if hiding was intended.

Это даже level 2. Если же переписать код так, как предлагает компилятор:
        public class B : A //, II
        {
            public new void Foo()
            {
                Console.WriteLine("B.Foo()");
            }
        }

то становится понятно, что B::Foo никакого отношения к A::Foo не имеет, в том числе не имеет отношения и к реализации методом A::Foo интерфейса.

Сделано же так во избежание возможных нечаянных конфликтов. Например, есть метод:
public void X() {
  var b = new B();
  Test(b);
}

public void Test(A a) {
  a.Foo(); // * Вызвался A::Foo - вызов не виртуальный
  var i = (II)a;
  i.Foo(); // ** А что здесь должно вызваться? A::Foo или B::Foo?
}


Так вот сейчас программист должен явно сказать, какое поведение ему нужно в точке (**). По умолчанию же (если явно не сказано другое) принимается самое очевидно и просто — вызов A::Foo, то есть того же метода, что и в (*). Если же требуется не очевидное на первый взгляд (с точки зрения использования, а не объявления!) поведение, то требуется при объявлении B явно указать, что B реализует II.
Help will always be given at Hogwarts to those who ask for it.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.