F # эквивалент C # typeof(IEnumerable<>)
-
21-09-2019 - |
Вопрос
У меня есть фрагмент кода, где мне нужно выяснить, реализует ли данный тип IEnumerable<T>
(Меня не волнует буква "Т")
Я пытался (t:System.Type
на случай, если вам интересно)
let interfaces = t.GetInterfaces()
let enumerbale =
interfaces.Any(fun t ->
t.GetGenericTypeDefinition() = typeof<IEnumerable<>>
)
однако это не будет компилироваться (компиляции не нравится <>).Затем я попробовал
let interfaces = t.GetInterfaces()
let enumerbale =
interfaces.Any(fun t ->
t.GetGenericTypeDefinition() = typeof<IEnumerable<'a>>
)
но get - это предупреждение о том, что 'a - это ограничение для obj.Я не хочу выяснять, если IEnumerable<obj>
реализован, но IEnumerabl<>
.
Любой знает, что это за решение, и, кстати, не стесняйтесь также комментировать приведенный выше код.
Решение
Это должно сработать:
typedefof<System.IEnumerable<_>>
Редактировать
Как отмечает Томас, нет ничего особенного в _
подстановочный знак здесь;F # выводит, что тип obj
является наиболее общим применимым типом в данном контексте, так что это то же самое, что использовать typedefof<System.IEnumerable<obj>>
.Однако в некоторых случаях то, как это работает, может быть небольшим препятствием.Например, если вы определяете интерфейс type I<'a when 'a :> I<'a>> = interface end
, тогда вы не сможете использовать typedefof<I<_>>
, потому что I<obj>
не удовлетворяет общему ограничению, и F # не может вывести другой, более подходящий тип.Это может произойти даже без рекурсивных ограничений (например type I<'a when 'a : struct and 'a :> System.ICloneable> = interface end
.Это в отличие от подхода C #, который отлично работает в аналогичных случаях.
Что касается самого вашего кода, я думаю, вы захотите внести и некоторые другие изменения, например, убедиться, что интерфейс является универсальным перед вызовом GetGenericTypeDefinition
.Вот как я бы написал тестовую функцию:
(fun t -> t.IsGenericType && (t.GetGenericTypeDefinition() = typedefof<_ seq>))
Другие советы
Насколько я знаю, F # не имеет никакого эквивалента C # 's typeof(IEnumerable<>)
.Это связано с тем, что это специальный синтаксис, явно поддерживаемый C #.В F#, typeof
это обычная функция, и аргумент type должен быть полностью заданного типа.Вы можете получить общее определение типа программно следующим образом:
let t = typeof<IEnumerable<obj>>
let genericT = t.GetGenericTypeDefinition()
Проблема с вашим решением с IEnumerable<'a>
заключается в том, что компилятору F # все еще необходимо найти какой-то конкретный тип для использования (поскольку определение универсального типа не является допустимым типом).Если вывод типа показывает, что параметр типа никоим образом не ограничен, он использует тип по умолчанию, который obj
.
Редактировать Я не знал о typedefof<IEnumerable<_>>
, это очень полезно!В любом случае, обратите внимание, что подчеркивание здесь не имеет никакого особого значения - фактический аргумент type по-прежнему IEnumerable<obj>
, но тот typedefof
вызовы функций GetGenericTypeDefinition
за сценой.
Я был бы упущением, если бы не указал, что этот вопрос является одним из многих, ответы на которые можно найти в