заключается в том, что оно предоставляет вид функциональности два-в-одном, что делает трудным соединение его с каким-либо одним назначением. Объявление класса как final запечатывает его, делая невозможным расширение. Объявление метода как final также его запечатывает, делая невозможным его переопределение. Объявление переменной как final является по сути объявлением ее только для чтения. Именно для чтения, а не константой, так как возможно задать значение final как значение переменной. Значения констант должны быть известны во время компиляции, поэтому константы могут задаваться равными только другим константам.

В противоположность этому C# предоставляет специфические приемы для рассмотрения каждого отдельного вопроса. По умолчанию подкласс не должен иметь возможности повторно реализовывать открытый метод, который уже реализован в суперклассе. C# вводит новую концепцию — сокрытие метода, что позволяет программисту переопределить члены суперкласса в классе-наследнике и скрыть реализацию базового класса, C# использует в данном случае модификатор new.

Это делается присоединением new к объявлению метода. Достоинство сокрытия версий членов базового класса состоит в том, что можно выборочно определить, какую реализацию использовать. Пример такой концепции представлен в коде ниже:

namespace Fona {

 using System;

 public class Plant {

  public Plant(){}

  public void BearFruit() {

   Console.WriteLine('Generic plant fruit');

  }

 }

 class Tree : Plant {

  public Tree(){}

  // ключевое слово new используется здесь явно, чтобы скрыть

  // базовую версию

  new public void BearFruit() {

   Console.WriteLine('Tree fruit is:->Mango');

  }

 }

 public class PlantEX {

  public PlantEX(){}

  public static void Main(String[] args) {

   Plant p = new Plant();

   p.BearFruit(); // вызывает реализацию базового класса

   Tree t = new Tree();

   t.BearFruit(); // вызывает реализацию класса наследника

   ((Plant)t).BearFruit(); // вызывает реализацию базового класса,

                           // используя наследника.

  }

 }

}

Выполнение этого примера создает следующий вывод:

Generic plant fruit

Tree fruit is:->Mango

Generic plant fruit

Необходимо отметить, что существует различие между сокрытием метода и обыкновенным полиморфизмом. Полиморфизм всегда будет предоставлять для вызова метод класса-наследника.

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

Чтобы предоставить функциональность переопределения, используются модификаторы virtual и override. Все методы в базовом классе, которые будут переопределяться, должны применить ключевое слово virtual. Чтобы реально переопределить их, в классе-наследнике используется ключевое слово override. Ниже представлен пример класса Tree, измененный для вывода переопределенной функциональности:

class Tree Plant {

 public Tree() {}

 public override void Fruit() {

  Console.WriteLine('Tree fruit is:->Mango');

 }

}

Компиляция и выполнение этого создают следующий вывод.

Generic plant fruit

Tree fruit is:->Mango

Tree fruit is:->Mango

Как можно видеть, вызывается самый последний переопределенный метод Fruit() независимо от использования cтратегии преобразования ((Plant)t).BearFruit(), которая применялась ранее для ссылки на метод Fruit() базового класса. Модификатор new может также использоваться для сокрытия любого другого типа наследованных из базового класса членов аналогичной сигнатуры.

Чтобы помешать случайному наследованию класса, используется ключевое слово sealed. В приведенном выше призере можно изменить объявление Plant на public sealed class Plant и в этом случае Tree больше не сможет от него наследовать.

C# не имеет модификатора native. В Java использование native указывает, что метод реализован на зависимом от платформы языке. Это требует чтобы метод был абстрактным, так как реализация должна находиться в другом месте. Ближайшим к этому типу функциональности является модификатор extern. Использование extern предполагает, что код реализуется вовне (например, некоторой собственной DLL). Однако в отличие от Java, нет необходимости использовать ключевое слово abstract в соединении с ним. Фактически это приведет к ошибке, так как они означают две похожие, но различные вещи. Ниже класс Plant из предыдущего примера показывает, как можно использовать extern:

public class Plant : IfruitHaver {

 public extern int See();

 public Plant(){}

 public void Fruit() {

  Console.WriteLine('Generic plant fruit');

 }

}

Это не имеет большого смысла без использования атрибута DllImport для определения внешней реализации. Более подробно атрибуты будут рассмотрены позже в приложении. Дальнейший код делает соответствующие изменения, предполагая, что функция See экспортирована ресурсом User32.dll:

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату