Вопрос

Следующая программа печатает

A:C(A,B)
B:C(A,B)

(как и должно быть)

public interface I
{
    string A();
}

public class C : I
{
    public string A()
    {
        return "A";
    }

    public string B()
    {
        return "B";
    }
}

public class A
{
    public virtual void Print(C c)
    {
        Console.WriteLine("A:C(" + c.A() + "," + c.B() + ")");
    }
}

public class B : A
{
    public new void Print(C c)
    {
        Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
    }

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }
}

class Program
{
    public static void Main(string[] args)
    {
        A a = new A();
        B b = new B();
        C c = new C();
        a.Print(c);
        b.Print(c);
    }
}

однако, если я изменю ключевое слово 'new' на 'override' в классе B следующим образом:

    public override void Print(C c)

внезапно программа начинает печатать:

A:C(A,B)
B:I(A)

Почему?

Это было полезно?

Решение

Это связано с тем, как разрешаются перегруженные методы.

Эффективно (несколько упрощенно), компилятор сначала просматривает объявленный тип выражения (B) в этом случае и ищет методы-кандидаты которые впервые объявлены в этом типе.Если есть какие-либо подходящие методы (т.е.где все аргументы могут быть преобразованы в типы параметров метода), то это не делает посмотрите на любые родительские типы.Это означает, что переопределенные методы, где первоначальное объявление находится в родительском типе, не просматриваются, есть ли какие-либо "недавно объявленные" соответствующие методы в производном типе.

Вот немного более простой пример:

using System;

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

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

    public void Foo(double d)
    {
        Console.WriteLine("Derived.Foo(double)");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        d.Foo(10);
    }
}

Это печатает Derived.Foo(double) - даже если компилятор знает, что существует метод сопоставления с параметром типа int, и аргументом является type int, и преобразование из int Для int является "лучше", чем преобразование из int Для double, тот факт , что только Foo(double) метод изначально объявленный в Derived означает, что компилятор игнорирует Foo(int).

Это в высшей степени удивительно, ИМО.Я могу понять, почему это было бы так, если бы Derived не переопределял Foo - в противном случае введение нового, более специфичного метода в базовый класс может неожиданно, но явно изменить поведение Derived здесь знает о нас Base.Foo(int) поскольку это перекрывает его.Это один из (относительно немногих) моментов, в которых, я считаю, разработчики C # приняли неправильное решение.

Другие советы

Хорошо, итак

    public new void Print(C c)
    {
        Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
    }

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }

При этом объявляется новый метод для печати.Теперь, поскольку B наследуется от A, вы просто вызываете новый метод дважды.Когда вы переопределяете метод, это изменяет сигнатуру метода при вызове A, но когда вы вызываете сигнатуру B, у нее появляется своя собственная сигнатура метода.

Я не уверен, объясняю ли я ясный, но хороший вопрос.

использование нового:

A и B получают одинаковую реализацию метода Print.

использование переопределения:

A имеет сигнатуру метода, отличную от B, поскольку вы не изменили сигнатуру метода в B только в A.

используя new, он в основном игнорирует это:

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }

Это был отличный вопрос.
Все ответы можно найти здесь:http://msdn.microsoft.com/en-us/library/6fawty39 (ПРОТИВ 80).aspx

Суть этого такова:

...компилятор C # сначала попытается сделать вызов совместимым с версиями [functionName], первоначально объявленными в [производном классе].Переопределяемые методы не считаются объявленными в классе, они являются новыми реализациями метода, объявленного в базовом классе.Только если компилятор C # не может сопоставить вызов метода с исходным методом в [производном классе], попытается ли он сопоставить вызов с переопределенным методом с тем же самым именем и совместимыми параметрами.

Итак, поскольку у вас есть новый метод Print(I i) для производного класса, который соответствует аргументу "c", (поскольку c реализует I), этот метод имеет приоритет над методом "переопределить".

Когда вы помечаете метод как "новый", они оба считаются реализованными в производном классе, а метод Print(C c) более точно соответствует параметру "c", поэтому он имеет приоритет.

Это, по крайней мере, такой же вопрос о том, как метод перегрузка работает на C #.Я думаю, вы выделили здесь интересную ситуацию...

В первом случае (используя new ключевое слово в методе), компилятор решает использовать Print перегрузка метода параметром типа C, поскольку его тип в точности эквивалентен типу переданного параметра (т.е.неявное преобразование не требуется), тогда как неявное преобразование в интерфейс I потребовалось бы, если бы компилятор выбрал Print метод, который принимает аргумент типа I - другими словами, он выбирает более "очевидную" перегрузку метода.

Во втором случае (используя override ключевое слово в методе), компилятор решает использовать перегрузку Print с параметром типа I, потому что, хотя вы переопределяете Print(C c) перегрузка метода в классе B, он эффективно определен в родительском классе A, что делает Print(I i) перегрузка метода на самом деле является перегрузкой самого высокого уровня и, следовательно, самой прямой, т.е.первый, который находит компилятор.

Надеюсь, это поможет вам понять.Дайте мне знать, если мне понадобится прояснить какие-либо моменты дополнительно...

Примечание:Если я ошибаюсь, говоря, что компилятор выполняет эти действия, то, пожалуйста, поправьте меня, хотя, казалось бы, для аргументации не имеет большого значения, является ли это компилятором или CLR / JIT.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top