Что такое утиная типизация (duck typing)?

Ответ

Утиная типизация — это концепция динамической типизации, при которой возможность использования объекта определяется не его явным типом (классом или интерфейсом), а наличием у него требуемых методов и свойств. Если объект имеет метод Quack(), его можно использовать как утку, даже если он не реализует интерфейс IDuck.

Контраст с номинативной типизацией (C#/Java): В строго типизированных языках совместимость типов проверяется на этапе компиляции по именам типов или интерфейсов.

Эмуляция в C# (где нет прямой поддержки):

  1. Использование dynamic (простейший способ, C# 4.0+):

    public void MakeDuckSound(dynamic duck)
    {
        // Проверка произойдёт во время выполнения (RuntimeBinderException при отсутствии метода)
        duck.Quack();
    }
    
    var realDuck = new RealDuck(); // Имеет метод Quack()
    var fakeDuck = new { Quack = new Action(() => Console.WriteLine("Quack!")) }; // Анонимный тип с методом
    var notADuck = new object();
    
    MakeDuckSound(realDuck); // OK
    MakeDuckSound(fakeDuck); // OK
    MakeDuckSound(notADuck); // RuntimeBinderException!
  2. Использование рефлексии (более гибко, но медленнее):

    public bool TryMakeDuckSound(object potentialDuck)
    {
        var quackMethod = potentialDuck.GetType().GetMethod("Quack", Type.EmptyTypes);
        if (quackMethod != null)
        {
            quackMethod.Invoke(potentialDuck, null);
            return true;
        }
        return false;
    }
  3. Использование сопоставления шаблонов (C# 8.0+, более безопасно):

    public void MakeDuckSound(object potentialDuck)
    {
        if (potentialDuck is IQuackable duck) // Проверка на интерфейс
        {
            duck.Quack();
        }
    }
    // Требует явного определения интерфейса
    public interface IQuackable { void Quack(); }

Плюсы и минусы:

  • Гибкость: Позволяет работать с объектами, тип которых неизвестен на этапе компиляции.
  • Опасность: Потеря безопасности типов, ошибки обнаруживаются только во время выполнения.
  • Производительность: Использование dynamic и рефлексии медленнее прямых вызовов.

Ответ 18+ 🔞

Слушай, вот эта утиная типизация — вообще прикол, если честно. Представь: тебе не важно, что за объект, главное чтобы он крякал. Есть метод Quack()? Ну значит ты утка, ёпта! Хоть ты хоть горшком назови, только в печь не суй.

Вот в этих строгих языках типа C# или Java — там всё по паспорту. «А ну покажи интерфейс IDuck! А, нету? Тогда пошёл нахуй, не утка». А по утиной типизации — «да похуй, братан, крякаешь? Крякаешь. Значит, добро пожаловать в утиный клуб».

В C# так прямо не сделаешь, но наши русские программисты — народ хитрый, жопа с ручками. Нашли лазейки.

Первый способ — через dynamic. Это вообще песня, я тебе скажу.

public void MakeDuckSound(dynamic duck)
{
    // А вот тут, сука, сюрприз может быть
    duck.Quack();
}

Берёшь любой объект — реальную утку, анонимный тип, да хоть бутылку водки — и суёшь в этот метод. Если у него есть Quack() — он крякнет. Если нет — получишь RuntimeBinderException прямо в ебало во время выполнения. Волнение ебать, как на русской рулетке.

Второй способ — рефлексия. Ну это классика, медленно, конечно, как наш интернет в деревне, но работает.

public bool TryMakeDuckSound(object potentialDuck)
{
    var quackMethod = potentialDuck.GetType().GetMethod("Quack", Type.EmptyTypes);
    if (quackMethod != null)
    {
        quackMethod.Invoke(potentialDuck, null); // Вызываем, блядь
        return true;
    }
    return false; // Не крякает — не утка
}

Ты просто шаришься по объекту, ищешь метод Quack. Нашёл — вызываешь. Не нашёл — ну и хуй с ним. Терпения ноль ебать, пока всё это отработает.

Третий способ — через интерфейсы и сопоставление шаблонов. Уже более цивилизованно, но уже не та чистота идеи.

public void MakeDuckSound(object potentialDuck)
{
    if (potentialDuck is IQuackable duck) // Проверяем, реализует ли интерфейс
    {
        duck.Quack();
    }
}

Тут уже нужен явный интерфейс IQuackable. То есть объект всё-таки должен как бы «зарегистрироваться» как утка. Не совсем то, но безопасно и быстро.

А теперь плюсы-минусы, как есть:

  • Гибкость — овердохуища. Можешь скормить методу что угодно, лишь бы крякало. Не надо плодить интерфейсы на каждый чих.
  • Опасность — тоже дохуя. Компилятор тебе не поможет, ошибка вылезет только когда программа уже работает. Представь, продакшен, ночь, всё падает — а потому что какой-то пидарас шерстяной передал тебе не ту сущность. Пиздец, короче.
  • Скорость — с dynamic и рефлексией будет медленнее, это как ехать на Запорожце вместо Ferrari. Но иногда и Запорожец — транспорт.

Короче, инструмент как молоток: можно гвоздь забить, а можно себе по пальцам ёбнуть. Смотри по обстановке.