Есть ли способ переопределить не виртуальный метод? или что-то, что дает аналогичные результаты (кроме создания нового метода для вызова желаемого метода)?
Я хотел бы переопределить метод Microsoft.Xna.Framework.Graphics.GraphicsDevice
с учетом модульного тестирования.
c#
overriding
overloading
Zfedoran
источник
источник
Ответы:
Нет, вы не можете переопределить не виртуальный метод. Самое близкое, что вы можете сделать, - это скрыть метод, создав
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). Я бы пошел еще дальше и сказал, что классы также должны быть запечатаны по умолчанию. Наследование сложно спроектировать должным образом, и тот факт, что существует метод, который не отмечен как виртуальный, указывает на то, что автор этого метода никогда не намеревался переопределить метод.
Изменить: «полиморфная отправка времени выполнения» :
Под этим я подразумеваю поведение по умолчанию, которое происходит во время выполнения, когда вы вызываете виртуальные методы. Скажем, например, что в моем предыдущем примере кода, вместо определения невиртуального метода, я фактически определил виртуальный метод и истинно переопределенный метод.
Если бы я позвонил
b.Foo
в этом случае, CLR правильно определила бы тип объекта, на которыйb
указывает ссылка,Bar
и отправила бы вызовM
соответствующим образом.источник
Нет, не можешь.
Вы можете переопределить только виртуальный метод - см. MSDN здесь :
источник
Если базовый класс не запечатан, вы можете унаследовать от него и написать новый метод, скрывающий базовый (используйте ключевое слово «new» в объявлении метода). В противном случае нет, вы не можете переопределить его, потому что первоначальные авторы никогда не намеревались его переопределить, поэтому он не виртуальный.
источник
Я думаю, вы путаетесь с перегрузкой и переопределением, перегрузка означает, что у вас есть два или более метода с одинаковым именем, но с разными наборами параметров, а переопределение означает, что у вас есть другая реализация для метода в производном классе (тем самым заменяя или изменяя поведение в его базовом классе).
Если метод виртуальный, вы можете переопределить его, используя ключевое слово override в производном классе. Однако невиртуальные методы могут скрыть базовую реализацию только с помощью ключевого слова new вместо ключевого слова override. Невиртуальный маршрут бесполезен, если вызывающий получает доступ к методу через переменную, набранную как базовый тип, поскольку компилятор будет использовать статическую отправку базовому методу (то есть код в производном классе никогда не будет вызван).
Никогда ничего не мешает вам добавить перегрузку в существующий класс, но только код, который знает о вашем классе, сможет получить к нему доступ.
источник
Вы не можете переопределить не виртуальный метод любого класса в C # (без взлома CLR), но вы можете переопределить любой метод интерфейса, который реализует класс. Считайте, что у нас негерметичный
class GraphicsDevice: IGraphicsDevice { public void DoWork() { Console.WriteLine("GraphicsDevice.DoWork()"); } } // with its interface interface IGraphicsDevice { void DoWork(); } // You can't just override DoWork in a child class, // but if you replace usage of GraphicsDevice to IGraphicsDevice, // then you can override this method (and, actually, the whole interface). class MyDevice: GraphicsDevice, IGraphicsDevice { public new void DoWork() { Console.WriteLine("MyDevice.DoWork()"); base.DoWork(); } }
А вот демо
class Program { static void Main(string[] args) { IGraphicsDevice real = new GraphicsDevice(); var myObj = new MyDevice(); // demo that interface override works GraphicsDevice myCastedToBase = myObj; IGraphicsDevice my = myCastedToBase; // obvious Console.WriteLine("Using real GraphicsDevice:"); real.DoWork(); // override Console.WriteLine("Using overriden GraphicsDevice:"); my.DoWork(); } }
источник
В случае, если вы наследуете непроизводный класс, вы можете просто создать абстрактный суперкласс и вместо этого наследовать от него ниже по течению.
источник
Вы не можете переопределить не виртуальный метод. Однако вы можете использовать
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());
Как указывалось в предыдущем ответе, это не лучший принцип дизайна.
источник
Есть способ добиться этого, используя абстрактный класс и абстрактный метод.
Рассмотреть возможность
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()
.Вы можете создать экземпляр (производного) класса по мере необходимости (т.е. либо с базовой реализацией, либо с реализацией модульного теста).
источник