- •Структуры и классы
- •Наследование реализации
- •Виртуальные методы
- •Сокрытие методов
- •Вызов базовых версий функций
- •Абстрактные классы и функции
- •Запечатанные классы и методы
- •Конструкторы производных классов
- •Добавление в иерархию конструктора
- •Добавление в иерархию конструкторов с параметрами
- •Интерфейсы
- •IDisposable — сравнительно простой интерфейс, потому что в нем определен только один метод. Большинство интерфейсов содержат гораздо большее количество методов.
- •Определение и реализация интерфейсов
- •Производные интерфейсы
Производные интерфейсы
Интерфейсы могут быть унаследованы друг от друга точно так же, как классы. Эта концепция иллюстрируется ниже определением нового интерфейса ITransferBankAccount, который обладает теми же возможностями, что и IBankAccount, но также определяет метод для перевода денег непосредственно на другой счет:
namespace Wrox.ProCSharp
{
public interface ITransferBankAccount: IBankAccount
{
bool TransferTo(IBankAccount destination, decimal amount);
}
)
Поскольку ITransferBankAccount наследуется от IBankAccount, наряду с собственными методами он получает все методы-члены IBankAccount. Это значит, что любой класс, реализующий (унаследованный от) ITransferBankAccount, должен реализовать все методы IBankAccount наряду с новым методом TransferTo(), определенным в ITransferBankAccount. Отсутствие реализации любого из этих методов приведет к ошибке компиляции.
Обратите внимание на то, что метод TransferTo() использует ссылку на интерфейс IBankAccount для указания целевого счета. Это иллюстрирует полезность интерфейсов. При реализации и последующем вызове метода вам не обязательно знать что-либо о типе объекта, которому переводятся деньги. Все, что необходимо знать это то, что объект реализует интерфейс IBankAccount.
Чтобы проиллюстрировать применение ITransferBankAccount, предположим, что Планетарный Банк Юпитера также предлагает текущий счет (Current Account). Большая часть реализации класса CurrentAccount идентична реализациям SaverAccount и GoldAccount (опять же, это только для простоты примера в реальности все далеко не так). Поэтому в следующем фрагменте выделена отличающаяся часть:
public class CurrentAccount: ITransferBankAccount
{
private decimal balance; public void Payln(decimal amount)
{
balance += amount;
}
public bool Withdraw(decimal amount)
{
if (balance >= amount)
{
balance -= amount;
return true;
}
Console.WriteLine("Попытка перевода денег не удалась.");
return false;
}
public decimal Balance
{
get
{
return balance;
}
}
public bool TransferTo(IBankAccount destination, decimal amount)
{
bool result;
result = Withdraw (amount) ;
if (result)
{
destination.Payln(amount);
}
return result;
}
public override string ToString()
{
return String.Format(
"Текущий счет в Банке Юпитера: Баланс = {0,6:С}", balance);
}
}
Класс можно протестировать с помощью следующего кода:
static void Main()
{
IBankAccount venusAccount = new SaverAccount ();
ITransferBankAccount jupiterAccount = new CurrentAccount();
venusAccount.Payln(200); jupiterAccount.Payln(500);
jupiterAccount.TransferTo(venusAccount, 100);
Console.WriteLine(venusAccount.ToString0) ;
Console.WriteLine(jupiterAccount.ToString() ) ;
)
Этот код (CurrentAccount.cs) генерирует следующий вывод, по которому можно убедиться в том, что перевод правильной денежной суммы выполнен:
С:> CurrentAccount
Сберегательный Банк Венеры: Баланс = £300.00
Текущий счет в Банке Юпитера: Баланс = £400.00
Итоги
В данном разделе было показано, как кодировать наследование в С#. Вы увидели, что C# предлагает богатую поддержку наследования множества интерфейсов и одной реализации. Также вы узнали о том, что в C# имеется множество полезных синтаксических конструкций, предназначенных для обеспечения большей устойчивости кода. Среди этих конструкций ключевое слово override, указывающее, когда функция должна переопределять базовую функцию, ключевое слово new, указывающее, что функция скрывает базовую функцию, а также жесткие правила инициализации конструкторов, предназначенные для обеспечения их устойчивого взаимодействия.