Ответ
Duck typing (утиная типизация) — это стиль динамической типизации, при котором объект считается подходящим для использования в определённом контексте, если он реализует все необходимые методы и свойства (имеет нужное "поведение"), а не на основе его явного наследования от конкретного класса или реализации интерфейса. Фраза происходит от идеи: "Если что-то ходит как утка и крякает как утка, то это, вероятно, и есть утка".
В C# утиная типизация реализуется несколькими способами:
-
Ключевое слово
dynamic: Полностью отключает проверку типов на этапе компиляции.public void MakeSound(dynamic animal) { // Проверка наличия метода Quack() произойдет во время выполнения. animal.Quack(); // RuntimeBinderException, если метода нет } // Вызов с любым объектом, имеющим метод Quack(), сработает. MakeSound(new RealDuck()); MakeSound(new ToyDuck()); -
Явные интерфейсы (статическая альтернатива): Предпочтительный способ в C#, когда контракт известен заранее. Это не утиная типизация в чистом виде, но решает ту же задачу — определение по поведению.
public interface ISoundMaker { void MakeSound(); } public class Duck : ISoundMaker { public void MakeSound() => Console.WriteLine("Quack!"); } public class Dog : ISoundMaker { public void MakeSound() => Console.WriteLine("Woof!"); } public void PerformSound(ISoundMaker maker) => maker.MakeSound(); -
Reflection: Позволяет проверить наличие членов объекта программно.
public bool CanQuack(object obj) { var method = obj.GetType().GetMethod("Quack"); return method != null && method.ReturnType == typeof(void) && !method.GetParameters().Any(); }
Плюсы:
- Высокая гибкость и возможность работы с объектами, типы которых не связаны общим предком.
Минусы (для dynamic и Reflection):
- Потеря безопасности типов на этапе компиляции.
- Риск ошибок времени выполнения (
RuntimeBinderException). - Снижение производительности.
- Отсутствие поддержки IntelliSense в IDE.
Вывод: В C# для статического контракта используйте интерфейсы. К dynamic прибегайте только при необходимости, например, при работе с COM, динамическими языками или JSON-объектами с неизвестной структурой.
Ответ 18+ 🔞
А, утиная типизация! Ну это ж классика, блядь. Сейчас объясню на пальцах, чтобы даже мартышке было понятно.
Представь, у тебя есть функция, которая просит: «Дай мне штуку, которая крякает». И тебе похуй, что это за объект — живая утка, резиновая игрушка или сосед Вася после пятой рюмки. Главное, чтобы при вызове .Quack() эта хрень издала нужный звук. Если ходит как утка и крякает как утка — пофиг на её паспорт и родословную. Вот и вся философия, в рот меня чих-пых!
В C# это можно провернуть несколькими способами, конечно. Но смотри, тут есть подводные грабли, о которые можно ебальник разбить.
Первый способ — через dynamic. Это как дать обезьяне гранату: мощно, но непредсказуемо.
public void ЗаставитьКрякнуть(dynamic птица)
{
// Компилятор тут просто отдыхает. Он поверит на слово.
птица.Quack(); // А вот тут, во время работы, может быть пиздец, если метода нет.
}
// Вызываешь с чем угодно
ЗаставитьКрякнуть(new НастоящаяУтка()); // Крякнет — красота.
ЗаставитьКрякнуть(new ИгрушечнаяУтка()); // И это сработает, если метод есть.
ЗаставитьКрякнуть(new Кот()); // RuntimeBinderException, блядь! Кот не крякает, мудак!
Плюс — гибкость овердохуища. Минус — безопасность типов на этапе компиляции летит в пизду. IDE тебе тоже не подскажет нихуя, будешь тыкать пальцем в небо.
Второй способ — интерфейсы. Это как раз умный, взрослый подход. Не совсем чистая «утиность», но задача решается.
public interface IЗвук { void ИздатьЗвук(); }
public class Утка : IЗвук { public void ИздатьЗвук() => Console.WriteLine("Кря-кря!"); }
public class Собака : IЗвук { public void ИздатьЗвук() => Console.WriteLine("Гав-гав!"); }
public void Концерт(IЗвук артист) => артист.ИздатьЗвук();
Всё чётко, компилятор проверяет, IntelliSense работает. Но нужно, чтобы классы заранее договорились реализовывать этот интерфейс.
Третий способ — рефлексия. Это уже для мазохистов и ситуаций, когда ты вообще нихуя не знаешь об объекте.
public bool УмеетКрякать(object подозрительныйОбъект)
{
var метод = подозрительныйОбъект.GetType().GetMethod("Quack");
// Проверяем, есть ли метод, без параметров и возвращает void
return метод != null && метод.ReturnType == typeof(void) && !method.GetParameters().Any();
}
Медленно, муторно, но иногда без этого никуда.
Итог, чувак: если знаешь контракт заранее — пили интерфейсы, не мудри. Если работаешь с какой-то внешней, ебучей динамической хуйней (типа JSON из интернета или скриптами) — тогда dynamic или рефлексия. Но помни: как только отключаешь проверку типов, ты идешь по охуенно тонкому льду. Один неверный шаг — и RuntimeBinderException тебя ебнет прямо в сраку.