Можно ли переопределить невиртуальный метод?
есть ли способ переопределить невиртуальный метод? или что-то, что дает аналогичные результаты (кроме создания нового метода для вызова нужного метода)?
Я хотел бы переопределить метод из Microsoft.Xna.Framework.Graphics.GraphicsDevice с модульным тестированием в виду.
7 ответов:
нет, вы не можете переопределить невиртуальный метод. Самое близкое, что вы можете сделать, это скрыть метод, создав
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метод printFoo.M. Как вы можете видеть этот подход позволяет вам иметь новую реализацию для метода, если ссылка на этот объект имеет правильный производный тип, но скрывает базовый метод тут перерыв полиморфизм.Я бы рекомендовал вам не скрывать базовые методы таким образом.
Я склоняюсь к тем, кто предпочитает поведение C#по умолчанию, что методы не являются виртуальными по умолчанию (в отличие от Java). Я бы пошел еще дальше и сказал, что классы также должны быть запечатано по умолчанию. Наследование трудно правильно спроектировать, и тот факт, что существует метод, который не помечен как виртуальный, указывает на то, что автор этого метода никогда не предназначался для переопределения метода.
Edit: "время выполнения полиморфной отправки":
что я имею в виду это поведение по умолчанию, которое происходит во время выполнения при вызове виртуальных методов. Допустим, например, что в моем предыдущем примере кода, а не определяя невиртуальный метод, я фактически определил виртуальный метод и истинный переопределенный метод.
если бы я позвонил
b.Fooв этом случае среда CLR правильно определит тип объекта, которыйbссылки на asBarи отправил бы вызовMнадлежащим образом.
Нет, ты не можешь.
вы можете переопределить только виртуальный метод-см. MSDN здесь:
в C# производные классы могут содержать методы с тем же именем, что и методы базового класса.
- метод базового класса должен быть определен как виртуальный.
Если базовый класс не запечатан, то вы можете наследовать от него и написать новый метод, который скрывает базовый (использование ключевого слова "new" в объявлении метода). В противном случае нет, вы не можете переопределить его, потому что он никогда не был первоначальным намерением авторов для его переопределения, поэтому он не является виртуальным.
Я думаю, что вы получаете перегрузку и переопределение путаницы, перегрузка означает, что у вас есть два или более методов с одинаковым именем, но разными наборами параметров, а переопределение означает, что у вас есть другая реализация для метода в производном классе (тем самым заменяя или изменяя поведение в его базовом классе).
Если метод является виртуальным, его можно переопределить с помощью ключевого слова override в классе derrived. Однако невиртуальные методы могут скрывать только базу реализация с помощью нового ключевого слова вместо ключевого слова переопределения. Невиртуальный маршрут бесполезен, если вызывающий обращается к методу через переменную, типизированную как базовый тип, поскольку компилятор будет использовать статическую отправку к базовому методу (что означает, что код в вашем классе derrived никогда не будет вызван).
ничто не мешает вам добавить перегрузку к существующему классу, но только код, который знает о вашем классе, сможет получить к нему доступ.
в случае, если вы наследуете от не производного класса, вы можете просто создать абстрактный суперкласс и наследовать от него вниз по течению.
есть ли способ переопределить невиртуальный метод? или что-то, что дает аналогичные результаты (кроме создания нового метода для вызова нужного метода)?
вы не можете переопределить невиртуальный метод. Однако вы можете использовать
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());вы также хотите, чтобы убедиться, что открыть модификатор тоже самое, иначе вы не получите наследство вдоль линии. Если другой класс наследует от
Class1thenewключевое слово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());как указывалось в предыдущем ответе, это не очень хороший принцип проектирования.
существует способ достижения этого с помощью абстрактного класса и абстрактного метода.
считают
Class Base { void MethodToBeTested() { ... } void Method1() { } void Method2() { } ... }теперь, если вы хотите иметь различные версии метода MethodToBeTested (), то измените базу классов на абстрактный класс и метод MethodToBeTested () как абстрактный метод
abstract Class Base { abstract void MethodToBeTested(); void Method1() { } void Method2() { } ... }С абстрактной пустоты MethodToBeTested() по какому-либо вопросу; осуществление ушла.
значит создать
class DefaultBaseImplementation : Baseиметь значение по умолчанию реализация.и создать еще один
class UnitTestImplementation : Baseдля осуществления проверки.С этими 2 новыми классами функциональность базового класса может быть переопределена.
Class DefaultBaseImplementation : Base { override void MethodToBeTested() { //Base (default) implementation goes here } } Class UnitTestImplementation : Base { override void MethodToBeTested() { //Unit test implementation goes here } }теперь у вас есть 2 класса реализации (переопределение)
MethodToBeTested().вы можете создать экземпляр (производного) класса по мере необходимости (т. е. либо с базовой реализацией, либо с реализацией модульного теста).
Comments