Можно ли переопределить не виртуальный метод?

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

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

Ответ 1

Нет, вы не можете переопределить не виртуальный метод. Самое близкое, что вы можете сделать, это скрыть метод, создав метод 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 распечатывают Foo.M. Как вы можете видеть, этот подход позволяет вам иметь новую реализацию для метода, если ссылка на этот объект имеет правильный производный тип, но скрытие базового метода нарушает полиморфизм.

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

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

Изменить: "Полиморфная отправка времени выполнения" :

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

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

Ответ 2

Нет, вы не можете.

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

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

  • Метод базового класса должен быть определен виртуальным.

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

Вы не можете переопределить не виртуальный метод. Однако вы можете использовать ключевое слово 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());

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

Ответ 7

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

Рассмотрим

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

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

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

abstract Class Base
{

     abstract void MethodToBeTested();

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

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

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

И создайте еще один class UnitTestImplementation : Base для реализации unit test.

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

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

}

Class UnitTestImplementation : Base
{

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

}

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

Вы можете создать экземпляр (производного) класса по мере необходимости (например, с базовой реализацией или с реализацией unit test).