C# → Перегрузка, сокрытие, перекрытие методов

Перекрытие методов

Пример перекрытия метода в C#:

public class myClass
{
  // виртуальный метод
  public virtual string ToString()  
  {
      return "";
   }
}

public class myDelivierClass : myClass
{
  // перегруженный, перекрытый метод
  public override string ToString()   
  {
    return "new return";
  }
}

Сокрытие методов

Даже если в базовом классе метод не был объявлен виртуальным, в производном классе все равно можно объявить другой метод с такой же сигнатурой. Новый метод однако не перекроет метод базового класса. Напротив, говорят, что он скроет метод базового класса. При этом компилятор, решая, какой метод вызвать, всегда будет рассматривать тип данных, на который указывает переменная, как тип данных заданный при ее объявлении!

Скрытие (переопределение) элементов класса – это явное описание в классе-наследнике с новыми характеристиками уже существующих элементов из наследуемого класса.

    public class MyClass
    {
        public int MyNumber;
    }

    public class OurClass : MyClass
    {
        public double MyNumber;
    }

Переопределено поле MyNumber (в родительском классе имело целочисельный тип, а в производном – дробный). Поле целого типа скрыто новым описанием. Если бы поле MyNumber не было переопределено, то в классе OurClass (производный) по умолчанию было бы доступен только родительское поле MyNumber типа int. А выражение: oc.MyNumber = 5.5; вызывало ошибку типов.

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

Если метод скрывает базового класса, то к его определению необходимо добавить ключевое слово new. Этот модификатор подскажет компилятору, что программист знает о факте сокрытия!

  public class Customer
  {
      public string GetFunnyString()
      {
                  return "Cusomer funny!";
      }
  }   
            ...
    public class Nevermore60Customer : Customer
    {
       public new string GetFunnyString()
       {
                 return "Nevermore60 funny!”;
       }
    }  
      ...
            Customer Cust1;
            Nevermore60Customer Cust2;
            Cust1 = new Customer();
            Console.WriteLine(Cust1.GetFunnyString());
            Cust1 = new Nevermore60Customer();
            Console.WriteLine(Cust1.GetFunnyString());
            Cust2 = new Nevermore60Customer();
            Console.WriteLine(Cust2.GetFunnyString());
      ...
}

Результат: Cusomer funny! Cusomer funny! Nevermore60 funny!

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

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

Если хотя бы один метод в классе абстрактный, то и весь класс должен быть абстрактным! Кроме того, любой класс, порожденный от абстрактного должен перекрыть его методы.

public abstract class Customer
{
  public abstract int MyAbstractMethod();     // тело отсуствует
  ...
  public class Nevermore60Customer : Customer
  {
    public override int MyAbstractMethod()
    {
       return 0;
    }
  }
}

Запечатанные (sealed) классы и методы можно рассматривать как противоположность абстрактным классам и методам. Объявление класса или метода запечатанным означает что произвести наследование и перекрытие не возможно.

sealed class FinalClass     // наследовать не возможно
...
      public class myClass
      {
       // перекрытие в дальнейшем не озможно
       public sealed override FinalMethod()    
      } 
...

Вызов базовых версий методов

Ключевое слово base явно указывает компилятору, что происходит обращение к методу базового класса.

public class CustomerAccount
    {
        public virtual decimal CalculatePrice (CustomerAccount account)
        {
            // ...
            return 2000M;
        }
    }

    public class GoldAccount : CustomerAccount
    {
        public override decimal CalculatePrice(CustomerAccount account)
        {
            return base.CalculatePrice(account) * 0.9M;
        }
    }

+ Индексаторы, операции, свойства можно делать виртуальными и перекрывать их по желанию.

+ Поля не могут быть объявлены виртуальными или перекрытыми. Однако можно скрыть базовую версию поля, объявив другое поле с тем же именем в производном классе с модификатором (new). Получить доступ к базовому полю можно через base.[имя_поля].

+ Статические методы не могут быть объявлены виртуальными, но могут быть скрытыми (new). Не имеет смысла объявлять виртуальным статический член вообще!

Перегрузка методов

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

C# не разрешает использование значений по умолчанию для параметров.

Пример перегрузки методов: Console.WriteLine();

  • dxc5ep

    прошу прощения но думаю стоит исправить не "// перегруженный, перекрытый метод"
    а"//переопределенный, перекрытый метод" — путаница в терминологии, меня это сбило с толку