Можно ли изменить поведение метода в классе-наследнике?

Ответ

Да, поведение метода можно изменить в производном классе, но механизм и семантика зависят от используемого подхода: полиморфное переопределение или скрытие метода.

1. Полиморфное переопределение (override): Используется, когда метод базового класса помечен как virtual или abstract. Это основа полиморфизма.

class Logger {
    public virtual void Log(string message) {
        Console.WriteLine($"[INFO] {message}"); // Базовая реализация
    }
}

class FileLogger : Logger {
    public override void Log(string message) {
        // Полностью новая реализация для файлового вывода
        File.AppendAllText("log.txt", $"{DateTime.Now}: {message}n");
    }
}

// Использование полиморфизма
Logger logger = new FileLogger();
logger.Log("Test"); // Вызовется FileLogger.Log

2. Скрытие метода (new): Применяется, если метод базового класса не virtual. Создаётся новый, независимый метод с тем же именем.

class Base {
    public void Display() => Console.WriteLine("Base.Display");
}

class Derived : Base {
    public new void Display() => Console.WriteLine("Derived.Display");
}

// Поведение зависит от типа ссылки
Base obj1 = new Derived();
obj1.Display(); // Вывод: "Base.Display" (вызван метод из Base)

Derived obj2 = new Derived();
obj2.Display(); // Вывод: "Derived.Display" (вызван метод из Derived)

Практические рекомендации:

  • Используйте override для реализации полиморфного поведения, когда разные типы объектов должны по-разному реагировать на один и тот же вызов метода.
  • Ключевое слово new следует применять осознанно, обычно при расширении сторонних классов, где вы не можете изменить базовый код. Это может привести к путанице, так как поведение зависит от типа переменной, а не от реального объекта.

Ответ 18+ 🔞

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

Если в базовом классе метод объявлен как virtual или abstract, то ты в наследнике можешь его полностью переписать под себя, и это будет честный полиморфизм. Все вызовы через базовый тип будут идти к твоей новой реализации — магия, блядь, в хорошем смысле.

class Logger {
    public virtual void Log(string message) {
        Console.WriteLine($"[INFO] {message}"); // Базовая, скучная реализация
    }
}

class FileLogger : Logger {
    public override void Log(string message) {
        // А вот тут уже твои правила: пишем в файл, а не в консоль
        File.AppendAllText("log.txt", $"{DateTime.Now}: {message}n");
    }
}

// И вот тут прикол: даже если переменная типа Logger, вызовется метод из FileLogger
Logger logger = new FileLogger();
logger.Log("Test"); // Сработает FileLogger.Log, потому что override

А теперь второй вариант, когда в базовом классе метод не виртуальный. Тут уже история, как в плохом анекдоте: ты можешь создать метод с таким же именем в наследнике, но это будет совершенно отдельный метод, который просто прячет родительский. Это как надеть парик и усы — вроде похож, но система тебя не узнает.

class Base {
    public void Display() => Console.WriteLine("Base.Display");
}

class Derived : Base {
    public new void Display() => Console.WriteLine("Derived.Display"); // Новый метод, старый спрятан
}

// И вот тут начинается цирк
Base obj1 = new Derived();
obj1.Display(); // Выведет "Base.Display", потому что смотрим через тип Base

Derived obj2 = new Derived();
obj2.Display(); // А тут уже "Derived.Display", потому что тип Derived

Короче, если хочешь нормального полиморфизма, когда объект сам решает, какой метод вызывать, — используй override. А new — это такой костыль на крайний случай, когда трогать базовый код нельзя, но очень хочется своё впихнуть. Только не удивляйся потом, когда поведение будет прыгать в зависимости от типа переменной — это ж твой выбор, ёпта.