Можно ли переопределить не виртуальный метод? - PullRequest
82 голосов
/ 06 декабря 2009

Есть ли способ переопределить не виртуальный метод? или что-то, что дает аналогичные результаты (кроме создания нового метода для вызова нужного метода)?

Я бы хотел переопределить метод из Microsoft.Xna.Framework.Graphics.GraphicsDevice с учетом модульного тестирования.

Ответы [ 7 ]

102 голосов
/ 06 декабря 2009

Нет, вы не можете переопределить не виртуальный метод. Самое близкое, что вы можете сделать, это скрыть метод, создав метод new с тем же именем, но это не рекомендуется, поскольку он нарушает принципы хорошего дизайна.

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

using System;

class Example
{
    static void Main()
    {
        Foo f = new Foo();
        f.M();

        Foo b = new Bar();
        b.M();
    }
}

class Foo
{
    public void M()
    {
        Console.WriteLine("Foo.M");
    }
}

class Bar : Foo
{
    public new void M()
    {
        Console.WriteLine("Bar.M");
    }
}

В этом примере оба вызова метода M print Foo.M. Как вы можете видеть, этот подход позволяет вам иметь новую реализацию для метода, если ссылка на этот объект имеет правильный производный тип, но скрывает базовый метод , который нарушает полиморфизм.

Я бы порекомендовал вам не скрывать базовые методы таким образом.

Я склонен поддержать тех, кто предпочитает поведение по умолчанию в C #, что методы не являются виртуальными по умолчанию (в отличие от Java). Я бы пошел еще дальше и сказал, что классы также должны быть запечатаны по умолчанию. Наследование сложно спроектировать должным образом, и тот факт, что существует метод, который не помечен как виртуальный, указывает на то, что автор этого метода никогда не предполагал переопределение метода.

Редактировать: "время исполнения полиморфной отправки" :

Под этим я подразумеваю поведение по умолчанию, которое происходит во время выполнения при вызове виртуальных методов. Скажем, например, что в моем предыдущем примере кода вместо определения не виртуального метода я фактически определил виртуальный метод и истинный переопределенный метод.

Если бы я в этом случае вызвал b.Foo, CLR правильно определил бы тип объекта, на который ссылка b указывает как Bar, и соответствующим образом отправил бы вызов на M.

21 голосов
/ 06 декабря 2009

Нет, ты не можешь.

Вы можете переопределить только виртуальный метод - см. MSDN здесь :

В C # производные классы могут содержать методы с тем же именем, что и методы базового класса.

  • Метод базового класса должен быть определен как виртуальный.
4 голосов
/ 06 декабря 2009

Если базовый класс не запечатан, вы можете наследовать его и написать новый метод, который скрывает базовый (используйте ключевое слово «new» в объявлении метода). Иначе нет, вы не можете переопределить его, потому что первоначальные авторы никогда не намеревались его переопределить, и поэтому он не является виртуальным.

2 голосов
/ 06 декабря 2009

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

Если метод виртуальный, вы можете переопределить его, используя ключевое слово override в классе derrived. Однако не виртуальные методы могут скрыть базовую реализацию только с помощью ключевого слова new вместо ключевого слова override. Не виртуальный маршрут бесполезен, если вызывающий обращается к методу через переменную, типизированную в качестве базового типа, поскольку компилятор будет использовать статическую диспетчеризацию для базового метода (то есть код в вашем классе вывода никогда не будет вызываться).

Ничто не мешает вам добавить перегрузку к существующему классу, но только код, который знает о вашем классе, сможет получить к нему доступ.

0 голосов
/ 15 сентября 2016

Есть ли способ переопределить не виртуальный метод? или что-то, что дает похожие результаты (кроме создания нового метода для вызова нужного метода)?

Вы не можете переопределить не виртуальный метод. Однако вы можете использовать ключевое слово модификатор new, чтобы получить аналогичные результаты:

class Class0
{
    public int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    public new int Test()
    {
        return 1;
    }
}
. . .
// result of 1
Console.WriteLine(new Class1().Test());

Вы также захотите убедиться, что модификатор доступа также не изменился, иначе вы не получите наследство в будущем. Если другой класс наследует от Class1, ключевое слово new в Class1 не повлияет на наследуемые от него объекты, если только модификатор доступа не является таким же.

Если модификатор доступа не , то же самое:

class Class0
{
    protected int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    // different access modifier
    new int Test()
    {
        return 1;
    }
}

class Class2 : Class1
{
    public int Result()
    {
        return Test();
    }
}
. . .
// result of 0
Console.WriteLine(new Class2().Result());

... в сравнении с модификатором доступа равным :

class Class0
{
    protected int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    // same access modifier
    protected new int Test()
    {
        return 1;
    }
}

class Class2 : Class1
{
    public int Result()
    {
        return Test();
    }
}
. . .
// result of 1
Console.WriteLine(new Class2().Result());

Как указывалось в предыдущем ответе, это не очень хороший принцип проектирования.

0 голосов
/ 08 марта 2016

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

0 голосов
/ 24 февраля 2015

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

Рассмотрим

Class Base
{
     void MethodToBeTested()
     {
        ...
     }

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

Теперь, если вы хотите иметь разные версии метода MethodToBeTested (), тогда измените Class Base на абстрактный класс и метод MethodToBeTested () как абстрактный метод

abstract Class Base
{

     abstract void MethodToBeTested();

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

С абстрактным void MethodToBeTested () возникает проблема; реализация ушла.

Следовательно, создайте class DefaultBaseImplementation : Base, чтобы иметь реализацию по умолчанию.

И создайте еще один class UnitTestImplementation : Base, чтобы иметь реализацию модульного теста.

С этими двумя новыми классами функциональность базового класса может быть переопределена.

Class DefaultBaseImplementation : Base    
{
    override void MethodToBeTested()    
    {    
        //Base (default) implementation goes here    
    }

}

Class UnitTestImplementation : Base
{

    override void MethodToBeTested()    
    {    
        //Unit test implementation goes here    
    }

}

Теперь у вас есть 2 реализующих (переопределяющих) класса MethodToBeTested().

При необходимости можно создать экземпляр (производного) класса (т. Е. Либо с базовой реализацией, либо с реализацией модульного теста).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...