2009-12-05 4 views
65

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

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

+7

Вы имеете в виду * overload * или * override *? Overload = добавить метод с тем же именем, но с разными параметрами (например, различные перегрузки Console.WriteLine). Override = (примерно) изменение поведения метода по умолчанию (например, метод Shape.Draw, который имеет другое поведение для Circle, Rectangle и т. Д.). Вы всегда можете * перегружать * метод в производном классе, но * переопределение * применяется только к виртуальным методам. – itowlson

ответ

81

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

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

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

Edit: «Время выполнения полиморфные диспетчерский»:

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

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

+4

Хотя «время полиморфной диспетчеризации времени» технически является правильным способом сказать это, я думаю, что это, вероятно, проходит через головы почти всех! –

+1

Хотя верно, что автор, возможно, намеревался отклонить метод, переопределяющий это неправда, что это было обязательно правильное дело. Я чувствую, что команда XNA должна была реализовать интерфейс IGraphicsDevice, чтобы предоставить пользователю больше гибкости при отладке и модульном тестировании. Я вынужден делать очень уродливые вещи, и это должно было предвидеть команда. Дальнейшее обсуждение можно найти здесь: http://forums.xna.com/forums/p/30645/226222.aspx – zfedoran

+0

@Orion - достаточно справедливо, я добавил объяснение :) –

17

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

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

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

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

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

3

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

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

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

-4

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

Рассмотрим

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

    void Method1() 
    { 
    } 

    void Method2() 
    { 
    } 

    ... 
} 

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

abstract Class Base 
{ 

    abstract void MethodToBeTested(); 

    void Method1() 
    { 
    } 

    void Method2() 
    { 
    } 

    ... 
} 

С абстрактной пустотой 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().

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

+0

@slavoo: Привет slavoo. Спасибо за обновление кода. Но не могли бы вы рассказать мне причину этого? – ShivanandSK

+3

Потому что он не отвечает на вопрос. Он спрашивает, можете ли вы переопределить членов, не отмеченных виртуальными. Вы продемонстрировали, что вам нужно реализовать элементы, отмеченные абстрактом. –

0

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

0

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

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

Вы также хотите, чтобы убедиться, что access modifier также же, в противном случае вы не получите наследство вниз линии.Если другой класс наследуется от 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()); 

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

Смежные вопросы