Что делать, если в одном классе реализуются два интерфейса с методами одинакового имени?

Ответ

Для разрешения конфликта имен методов из разных интерфейсов используется явная реализация интерфейса (explicit interface implementation). Этот подход позволяет предоставить отдельные реализации для каждого интерфейса.

Ключевые моменты:

  • Метод, реализованный явно, не имеет модификатора доступа (public, private и т.д.).
  • Вызвать такой метод можно только через переменную, приведенную к типу конкретного интерфейса.
  • Это единственный чистый способ в C# решить данную проблему без изменения сигнатур методов в интерфейсах.

Пример:

interface ILogger
{
    void Write(string message);
}

interface IDebugger
{
    void Write(string message);
}

class MultiWriter : ILogger, IDebugger
{
    // Стандартная (неявная) реализация для ILogger
    public void Write(string message)
    {
        Console.WriteLine($"[LOG] {message}");
    }

    // Явная реализация для IDebugger
    void IDebugger.Write(string message)
    {
        Console.WriteLine($"[DEBUG] {message}");
    }
}

// Использование:
var writer = new MultiWriter();

// Вызывает реализацию ILogger.Write
writer.Write("Application started");

// Для вызова IDebugger.Write требуется приведение
((IDebugger)writer).Write("Checking state");

// Или через переменную интерфейсного типа
IDebugger debugWriter = writer;
debugWriter.Write("Debug info");

Практический совет: Используйте явную реализацию, когда метод имеет разную семантику в разных интерфейсах, и вы хотите четко разделить это поведение в коде.

Ответ 18+ 🔞

Давай разжую эту тему, а то некоторые думают, что если два интерфейса говорят "напиши Write", то можно один метод нарисовать и всем угодить. Ага, щас, как же! Получишь конфликт имён, и компилятор тебе такое предъявит, что мало не покажется.

Вот смотри, в чём суть. Есть у тебя два интерфейса, и в них метод называется одинаково — void Write(string message). Класс должен реализовать оба. Если ты просто попробуешь написать один публичный метод Write, то компилятор офигеет: "Чувак, это реализация для кого? Для ILogger или для IDebugger? Я нихуя не понимаю!"

И тут на сцену выходит явная реализация интерфейса — наш спаситель, блядь. Это такой специальный синтаксис, где ты привязываешь конкретную реализацию метода к конкретному интерфейсу, и делаешь это втайне от общего API своего класса.

Как это выглядит? А вот как, смотри:

interface ILogger
{
    void Write(string message);
}

interface IDebugger
{
    void Write(string message);
}

class MultiWriter : ILogger, IDebugger
{
    // Это — обычная, неявная реализация. Она считается реализацией для ILogger.
    // Её можно вызвать просто так, с объекта.
    public void Write(string message)
    {
        Console.WriteLine($"[LOG] {message}");
    }

    // А это — явная реализация для IDebugger.
    // Видишь? Никакого модификатора доступа (public/private)!
    // И имя метода — это полное имя интерфейса + точка + имя метода.
    void IDebugger.Write(string message)
    {
        Console.WriteLine($"[DEBUG] {message}");
    }
}

Теперь самый сок, как этим пользоваться:

var writer = new MultiWriter();

// Эта строка вызовет обычный публичный метод, то есть реализацию для ILogger.
writer.Write("Application started"); // Выведет: [LOG] Application started

// А чтобы вызвать метод из IDebugger, нужно привести объект к этому интерфейсу.
// Иначе этот метод просто спрятан, как грех за пазухой.
((IDebugger)writer).Write("Checking state"); // Выведет: [DEBUG] Checking state

// Или так, через переменную нужного типа — ещё понятнее.
IDebugger debugWriter = writer;
debugWriter.Write("Debug info"); // Выведет: [DEBUG] Debug info

Вот и вся магия, ёпта. Явная реализация — это как потайной карман. Метод есть, но добраться до него можно только через "дверь" конкретного интерфейса. Прямо с объекта класса его не вызовешь, он там невидимый.

Когда это реально полезно? Ну, например, когда у интерфейсов методы с одним именем, но смысл у них разный, как в нашем примере: логирование и отладка — это же две большие разницы, блядь. Или представь интерфейсы ISaveToFile и ISaveToDatabase, оба с методом Save(). Логично же сделать разную реализацию для каждого случая, а не один метод, который пытается быть и тем, и другим, превращаясь в говнокод.

Так что запомни: когда компилятор начинает ныть про неоднозначность — не паникуй, не пытайся переименовывать методы в интерфейсах (это обычно пиздец и нарушение контрактов). Просто сделай явную реализацию для одного из интерфейсов, или для обоих, и живи спокойно. Всё гениальное просто, как три копейки.