Вопрос по C# на собеседовании

Что будет выведено после запуска следующей программы и почему:

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Derived();
            int x = 123;
            a.Foo(x);
        }
    }

    public class Base
    {
        public virtual void Foo(int x)
        {
            Console.WriteLine("Base::Foo");
        }
    }

    public class Derived : Base
    {
        public override void Foo(int x)
        {
            Console.WriteLine("Derived::Foo(int x)");
        }

        public void Foo(object o)
        {
            Console.WriteLine("Derived::Foo(object o)");
        }
    }
}

Что бы вы ответили на данный вопрос?

Читайте также:

23 thoughts on “Вопрос по C# на собеседовании

  1. anonim

    я тоже могу скомпилировать и запустить программу. Но почему будет напечатан именно этот текст, а не другой?

    Reply
  2. anonim

    В данном примере возможность virtual потенциально описана, но в коде Main не использована.

    Код “var a = new Derived();” для переменной “а” в памяти отводит место как для Derived, значит a.Foo() вызовет собственный метод класса Derived
    и вернет: “Derived::Foo(object o)”

    Сноска:
    * для виртуального метода вызывается метод, соответствующий типу объекта, на который имеется ссылка;
    * для невиртуального метода вызывается метод, соответствующий типу самой ссылки.

    Если под переменную в памяти отвести место как под Base, но загрузить в неё объект Derived,
    то смысл модификатора virtual будет реализован и вызовется перекрытый метод.

    Например:
    Base b = new Derived();
    b.Foo(x);
    вернет “Derived::Foo(int x)”

    Reply
  3. anonim

    Никак не могу понять. При поиске перегруженого метода приоритет имеют неперекрытые методы? Объясните подробней пожалуйста.

    Reply
  4. anonim

    в том то и дело что для ссылки типа Derived виртуальный метод Foo перекрыт и вызыватся в таком случае будет перекрывающий метод. Перегрузка к этому не имеет отношения.

    Reply
  5. anonim

    Виртуальный метод перекрыт. А в итоге вызывается метод, который не перекрыт, а является новым в наследнике.

    Программа выводит: “Derived::Foo(object o)”

    Reply
  6. anonim

    хотя в разных книгах статьях по разному, скрытие перекрытие но override покрайнемере всюду на msdn – переопределение

    Reply
  7. anonim

    В классе Derived есть два метода (перегруженных, overload)
    В метод передаётся int, следовательно резолвер перегруженых методов должен выбрать наиболее подходящий Foo(int x)
    Но он выбирает метод с object. Видимо потому что метод с int является перекрытым (override). Если вместо override написать new, то будет вызываться метод c int.
    Вопрос, почему так и логично ли это?

    Reply
  8. anonim

    Совершенно логично кстати очень хорошая задача на знание особеннойстей полиморфизма с#
    Я плохо излагаю мысли но постораюсь объяснить, в этих двух классах вообще нет перегрузок. Определенный в родителе метод является виртуальным(virtual) следовательно его реализация зависит от объекта на который он ссылается переменная. В классе предке виртуальный метод переопределен(override), но также в этом классе есть метод с таким же названием и другой сигнатурой он не перегружает метод Foo т.к. не иммеет ключевого слова override а скрывает его для переменных типа Derived и вызыватся будет в данном случае он, неявное преобразование к типу обжект возможно для любого типа, а вот если бы создавалась переменная типа Base и ей присваивался объект типа Derived то вызывался бы метод переопределенный предком а не скрытый.

    Reply
  9. anonim

    Спасибо большое за объяснения. Но я к сожалению так и не пойму почему при вызове метода Derived::Foo, Выбирается метод с object, а не с int. Не могу понять при чем тут класс Base и то что метод помечен как override? У класса Derived есть два метода с одим именем и разными параметрами, разве это не перегрузка? Почему нет? (Разве это важно что один из методов был переопределен у родителя? Это как-то отключает перегрузку? (overload))
    Где можно почитать про такое поведение? Страница спецификации может быть?

    Reply
  10. anonim

    Узнал причину на одном из форумов:

    1. Overrides are not included in the set of candidate methods during
    resolution. This may seem counterintuitive since an override is more specific
    and therefore a better match. However, an override is logically the same
    method as the original declared in the base class. The original is still a
    candidate after we remove the overrides. If the remaining steps in the
    resolution process select the original method (i.e. the virtual version),
    dynamic binding will ensure that the correct override version executes.

    2. All candidate methods must come from the same type. We use the lowest
    type in the hierarchy that contains an applicable method. We would prefer to
    select a method from the lowest type since it is the most specific and we
    assume it would perform the most appropriate operation. Only if the lowest
    type does not contain an applicable method do we look further up the
    hierarchy. We keep all the methods declared in that type and discard all
    methods declared in all other types. We discard methods in the other types
    even if their parameter list is a better match.

    Reply
  11. anonim

    вот перегрузка метода переопределяющего виртуальный метод родителя к примеру
    public virtual void Foo(int x){….}
    public virtual void Foo(double x){….}
    а вот перегрузка метода скрывающего метод который переопределяет виртуальный метод родителя )))
    public void Foo(object o){….}
    public void Foo(byte o){….}
    в том примере перегрузок нет
    и кстати говоря по хорошему в скрывающем методе надо писать new если не писать компилятор выдает предупреждение и сам это делает. не знаю где лучше почитать MSDN,RSDN ) полиморфизм наследование виртуальные методы … кстати что за контора если не секрет

    Reply
  12. anonim

    Я прекрасно разбираюсь в сокрытии, в перегрузке и переопределении. Я только понятия не имел, что из кандидатов к резолву перегрузки выкидываются overrideнутые методы.
    Ваше описание я к сожалению так и не понял.

    Reply
  13. anonim

    Они не выкидываются, здесь даже намека нет на перегрузку это методы разных классов о – “an override is logically the same method as the original declared in the base class.”

    Reply
  14. anonim

    Например мой класс использует кто-то другой. И я вдруг решил один из методов перенести в базовый класс. Это поламает сторонний код, который использует мой класс. Вывод: надо везде использовать интерфейсы. Но это всё очень не очевидно для меня.

    Reply
  15. anonim

    не понял) зачем что то переносить методы наследуются а реализация виртуального метода зависит от типа объекта, грубо говоря можно в родительском классе использовать методы которые будут определены в дочерних

    Reply
  16. anonim

    Решил перенести метод из наследника в базовый класс. Рефакторинг такой. Решил что так лучше будет, так как кто-то ещё может этот метод использовать. А в наследнике сделал override.
    Получается я этим действием ломаю работающую схему механизма резолва перегрузки.
    Там где заказчик вызывал мой метод Foo, он получал int, а теперь только из-за того, что я внутри своей реализации сместил метод с int в базовый класс и переопределил его в наследнике у заказчика станет вызываться метод object. Заказчик рад не будет, и не будет понимать почему это произошло, ведь как было два метода в Derived так и осталось.

    Reply
  17. anonim

    Зачем в таком случае вообще объявлять метод виртуальным и писать override в наследнике? Два метода но один помечен override а другой нет, если вам нужно просто задать свою реализацию для данного потомка можно скрыть метод.Этот пример явная бессмыслица и призван запутать, в жизни такое писать врятли будет необходимость.

    Reply
  18. anonim

    У меня есть метод в базовом классе, базовый класс я менять не могу. Мне надо в дочернем классе создать перегрузку для этого метода. Как вы посоветуете действовать?
    Пока что я выкручиваюсь тем, что создаю интерфейс для дочернего класа, в котором есть оба метода, и вызываю методы через интерфейс, тогда резолвится всё так как и предполагается.
    Но если кто-то вызовет мой класс не через интерфейс, то будет резолвиться по другому.

    Reply
  19. anonim

    например
    public class Base
    {
    public void Foo(int x)
    {
    Console.WriteLine(“Base::Foo”);
    }
    }

    public class Derived : Base
    {

    public void Foo(string o)
    {
    Console.WriteLine(“Derived::Foo(string o)”);
    }
    }
    ну это не совсем перегрузка) сначала если вызывается метод Foo в классе Derived если количество переменных одинаково а типы разные он проверит возможно ли неявное преобразование, если это невозможно он вызовет подходящий метод из родителя

    Reply