Реализация безопасной утиной печати на C #

После того, как я посмотрел, как Go обрабатывает интерфейсы, и мне это понравилось, я начал думать о том, как можно добиться подобного утиного набора текста на C #, например это:

var mallard = new Mallard(); // doesn't implement IDuck but has the right methods
IDuck duck = DuckTyper.Adapt<Mallard,IDuck>(mallard);

Метод DuckTyper.Adapt будет использовать System.Reflection.Emit для создания адаптера на лету. Может, кто-нибудь уже писал что-то подобное. Думаю, это не слишком отличается от того, что уже делают фреймворки для фиксации.

Однако это вызовет исключения во время выполнения, если Mallard на самом деле не имеет правильных IDuck методов. Чтобы получить ошибку раньше во время компиляции, мне пришлось бы написать MallardToDuckAdapter, чего я и пытаюсь избежать.

Есть ли способ лучше?

edit: очевидно, правильным термином для того, что я называю "безопасный набор текста", является структурный печатать.


person Wim Coenen    schedule 13.11.2009    source источник
comment
Я хотел этого раньше, адаптируя систему System.xml.serialization для работы с универсальными форматерами system.runtime.serialization.   -  person Joel Coehoorn    schedule 13.11.2009
comment
Также: проверяет ли Go это во время компиляции или просто во время выполнения? Если последнее, они не предоставили никаких новых решений проблемы, только дополнительный код Adapt, который вы уже упомянули.   -  person Joel Coehoorn    schedule 13.11.2009
comment
Да, написание адаптера не будет считаться утиной типизацией, потому что это обычная типизация C # ...   -  person R. Martinho Fernandes    schedule 13.11.2009


Ответы (4)


Как узнать, ходит ли корова как утка и крякает как утка, если перед вами нет живой, дышащей коровы?

Утиная печать - это концепция, используемая во время выполнения. Аналогичная концепция во время компиляции - это структурная типизация, которая AFAIK не поддерживается CLR. (В основе CLR лежит номинативный ввод.)

[Система структурных типов] контрастирует с номинативными системами, где сравнения основаны на явных объявлениях или именах типов, и утиной типизации, в которой только часть структуры, к которой осуществляется доступ во время выполнения, проверяется на совместимость.

Обычный способ убедиться, что утиная типизация не вызывает исключений во время выполнения, - это модульные тесты.

person dtb    schedule 13.11.2009
comment
В Go утка-типизация проверяется статически. Компилятор смотрит на класс Cow и жалуется, если у него нет методов утки. - person Wim Coenen; 13.11.2009
comment
Это и есть определение структурного наследования :-) - person dtb; 13.11.2009
comment
В .NET утиная типизация используется иначе, чем в обычном значении этого термина. К сожалению, ребрендинг установленных технических терминов стал чем-то вроде корпоративного хобби в Microsoft (или, по крайней мере, в отделе CLR), что очень раздражает. - person Konrad Rudolph; 13.11.2009
comment
Ах, теперь я понимаю, что вы имеете в виду. Это называется структурной типизацией, а не структурным наследованием. en.wikipedia.org/wiki/Structural_typing - person Wim Coenen; 13.11.2009
comment
Ооо. Ты прав. У меня даже есть эта книга (Типы и языки программирования, рекомендуется !!) на моей полке, хотя я давно к ней не прикасался ... - person dtb; 13.11.2009
comment
Заметка из будущего (но, похоже, это было уже фактом еще в 2009 году): оба F # и Scala имеют структурные типы без какой-либо прямой поддержки со стороны виртуальной машины. - person rsenna; 27.02.2014

DuckTyping для C #

Reflection.Emit используется для испускания IL, который напрямую вызывает исходный объект.

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

person Bob    schedule 13.11.2009
comment
Выпущено под лицензией Artistic License 2.0! - person R. Martinho Fernandes; 13.11.2009
comment
+1 Мне нравится идея использовать здесь юнит-тесты. Таким образом, вы обмениваете (потенциально большие) накладные расходы на написание класса адаптера на небольшие накладные расходы на написание однострочного модульного теста. - person Wim Coenen; 13.11.2009

Я не думаю, что есть другой способ получить ошибку времени компиляции.

Однако это то, для чего отлично подходит модульное тестирование. Вы бы написали модульный тест, чтобы убедиться, что

DuckTyper.Adapt<Mallard, IDuck>(mallard);

успешно отображает.

person Joseph    schedule 13.11.2009

Я знаю, что неявные интерфейсы (а это и есть интерфейсы Go) были запланированы для VB 10 (понятия не имею о C #). К сожалению, они были отменены перед выпуском (я думаю, что они даже не дошли до бета-версии…). Было бы неплохо увидеть, появятся ли они в будущей версии .NET.

Конечно, новые типы dynamic могут использоваться для достижения того же самого, но это все еще не то же самое - неявные интерфейсы по-прежнему допускают строгую типизацию, что я считаю важным.

person Konrad Rudolph    schedule 13.11.2009
comment
Не могли бы вы предоставить ссылки, подтверждающие ваше утверждение относительно неявных интерфейсов в VB? - person Dario; 16.11.2009