Поиск

Избежание неоднозначности имен

Одна из главных причин, по которой С# не поддерживает множестве"-ное наследование, — проблема конфликта имен, результатом котором является неоднозначность имен. Хотя С# не поддерживает множественное наследование на уровне объектов (создание производных классов), он поддерживает наследование от одного класса и дополнительную реализацию нескольких интерфейсов. Однако за дополнительные возможности приходится расплачиваться конфликтами имен. I

Ниже интерфейсы ISerializable и IDataStore поддерживают чтение и хранение данных в разных форматах: в двоичной форме в виде объектов для хранения на диске и для хранения в БД. Проблема в том, что рни оба содержат методы с именем SaveData:

using System;
interface ISerializable {
void SaveDataO; }
interface IDataStore {
void SaveDataQ; }
class Test : ISerializable, IDataStore {
public void SaveDataO
{
Console.Writel_ine("Test. SaveData called");
} }
class NameCollisionslApp {
public static void Main()
{
Test test = new Test();
Console.WriteLine("Calling Test.SaveDataO"); ! test. SaveDataO;
\ >
>|

\ Во время написания этот код компилировался. Однако, как я уже говорил, в будущей версии компилятора С# этот код вызовет ошибку периода компиляции из-за неоднозначности реализованного метода Save-Ddta. Независимо от того, компилируется ли этот код, у вас будет про-бл^ма, так как поведение в результате вызова метода SaveData будет нео-пр^деленным для программиста, пытающегося задействовать этот класс. Получите ли вы метод SaveData, который последовательно за-пвдывает данные объекта на диск, или метод SaveData, который сохраняет их в БД?

В дополнение взгляните на такой код:

using System;
interface ISerializable {
void SaveDataO;
>
interface IDataStore {
void SaveDataO; }
class Test : ISerializable. IDataStore <
public void SaveDataO
{
Console.WriteLine("Test.SaveData called");
> }
class NameCollisions2App {
public static void Main()
{
Test test = new Test();
if (test is ISerializable)
{
Console.WriteLineC'ISerializable is implemented"); I
> / if (test is IDataStore)
< / Console.WriteLine("IDataStore is implemented"); /
}
> I }

В этом примере оператор is успешно выполняется в обоих интерфейсах, а значит, реализованы оба интерфейса, хотя мы знаем, что'это не так! При компиляции данного примера компилятор даже выдаст предупреждения:

NameCollisions2.cs(27,7): warning CS0183: The given expression is always of
the provided ('ISerializable') type NameCollisions2.cs(32,7): warning CS0183;
The given expression is always of the provided ('IDataStore') type

Проблема в том, что в классе реализованы или сериализованная версия метода Bind, или версия, использующая БД (но не обе сразу). Однако клиент получит положительный результат при проверке на реализацию обеих версий этого интерфейса. При попытке задействовать интерфейс, который на самом деле не реализован, получится непредсказуемый результат.

Чтобы решить эту проблему, можно обратиться к явной квалификации имени члена: уберем модификатор доступа и поставим перед именем члена (в данном случае перед SaveData) имя интерфейса:

using System;
interface ISerializable {
void SaveDataQ;
}
interface IDataStore
{
void SaveDataO;
}
class Test : ISerializable, IDataStore {
void ISerializable.SaveDataO
{
Console.WriteLine("Test.ISerializable.SaveData called");
}
void IDataStore. SaveDataO
{
Console.WriteLine("Test.IDataStore.SaveData called");
} }
class NameCollisionsSApp {
public static void MainO {
Test test = new Test(); if (test is ISerializable) {
Console.WriteLlne("ISerializable is Implemented");
((ISe rializable)test). SaveDataO; >
Console.WriteLineO; if (test is IDataStore) {
Console.WriteLine("IDataStore is implemented");
((IDataSto re)test).SaveData(); } } }

Теперь можно сказать однозначно, какой метод будет вызван. Оба метода реализованы с полностью квалифицированными именами, а приложение выдает именно тот результату что вы ожидаете:

ISerializable is implemented Test.ISe rializable.SaveData called
IDataStore is implemented Test.IDataStore.SaveData called