Полиморфизм семейства классов Found и Derived
Вернемся к нашему примеру с классами Found и Derived. В классе Found определены два виртуальных метода. Один из них - виртуальный метод VirtMethod - определен в самом классе, другой - виртуальный метод ToString - наследован от родительского класса object и переопределен в классе Found. Потомок класса Found - класс Derived переопределяет оба метода, соблюдая контракт, заключаемый в этом случае между родителем и потомком. При переопределении виртуального метода сохраняется имя метода и его сигнатура, изменяется лишь реализация: public override string ToString() { string s = "Поля: name = {0}, Basecredit = {1}" + "credit = {2}, debit = {3}"; returnString.Format(s, name, base.credit, credit, debit); } public override string VirtMethod() { return "Derived: " + this.ToString(); }
В классе Found определены два не виртуальных метода NonVirtMethod и Job, наследуемые потомком Derived без всяких переопределений. Вы ошибаетесь, если думаете, что работа этих методов полностью определяется базовым классом Found. Полиморфизм делает их работу куда более интересной. Давайте рассмотрим в деталях работу метода Job: public string Job() { string res = ""; res += "VirtMethod: " + VirtMethod() + NL; res += "NonVirtMethod: " + NonVirtMethod() + NL; res += "Parse: " + Parse() + NL; returnres; } При компиляции метода Job будет обнаружено, что вызываемый метод VirtMethod является виртуальным, поэтому для него будет применяться динамическое связывание. Это означает, что вопрос о вызове метода откладывается до момента, когда метод Job будет вызван объектом, связанным с x. Объект может принадлежать как классу Found, так и классам Derived и ChildDerived, и в зависимости от класса объекта и будет вызван метод этого класса. Для вызываемых методов NonVirtMethod и Parse, не являющихся виртуальными, будет применено статическое связывание, так что метод Job всегда будет вызывать методы, принадлежащие классу Found. Однако и здесь не все просто. Метод NonVirtMethod publicstringNonVirtMethod() { return "Found: " + this.ToString(); } в процессе своей работы вызывает виртуальный метод ToString. Опять-таки, для метода ToString будет применяться динамическое связывание, и в момент выполнения будет вызываться метод объекта, а не метод ссылки. Что же касается метода Parse, определенного в каждом классе, то всегда в процессе работы Job будет вызываться только родительский метод разбора из-за стратегии статического связывания. Хочу обратить внимание на важный принципиальный момент. Вполне понятно, когда потомки вызывают методы родительского класса. Потомкам все известно о своих предках. Но благодаря полиморфизму методы родительского класса, в свою очередь, могут вызывать методы своих потомков, которых они совсем не знают и которые обычно и не написаны в момент создания родительского класса. Достигается это за счет того, что между родителями и потомками заключается жесткий контракт. Потомок, переопределяющий виртуальный метод, сохраняет сигнатуру метода, сохраняет атрибуты доступа, изменяя реализацию метода, но не форму его вызова. Класс Found, создающий метод Job, говорит примерно следующее: "Я предоставляю этот метод своим потомкам. Потомок, вызвавший этот метод, должен иметь VirtMethod, выполняющий специфическую для потомка часть работы; конечно, потомок может воспользоваться и моей реализацией, но допустима и его собственная реализация. Затем часть работы выполняю я сам, но выдача информации об объекте определяется самим объектом. Заключительную часть работы, связанную с анализом, я потомкам не доверяю и делаю ее сам".
|