Использование typeof <_> в активном образце - PullRequest
5 голосов
/ 21 июля 2011

С учетом следующего надуманного активного шаблона:

let (|TypeDef|_|) (typeDef:Type) (value:obj) =
  if obj.ReferenceEquals(value, null) then None
  else
    let typ = value.GetType()
    if typ.IsGenericType && typ.GetGenericTypeDefinition() = typeDef then Some(typ.GetGenericArguments())
    else None

Следующее:

let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef typedefof<Dictionary<_,_>> typeArgs -> printfn "%A" typeArgs
| _ -> ()

выдает ошибку:

Неожиданное приложение типа при сопоставлении с образцом. Ожидаемый '->' или другой токен.

Но это работает:

let typ = typedefof<Dictionary<_,_>>
match dict with
| TypeDef typ typeArgs -> printfn "%A" typeArgs
| _ -> ()

Почему typedefof (или typeof) здесь не разрешен?

Ответы [ 2 ]

4 голосов
/ 21 июля 2011

Если добавить к ответу Томаса, в этом случае проблемный синтаксис выглядит как аргументы явного типа.Другой обходной путь - использовать фиктивный параметр для передачи информации о типе

let (|TypeDef|_|) (_:'a) (value:obj) =
  let typeDef = typedefof<'a>
  if obj.ReferenceEquals(value, null) then None
  else
    let typ = value.GetType()
    if typ.IsGenericType && typ.GetGenericTypeDefinition() = typeDef then Some(typ.GetGenericArguments())
    else None

let z = 
    let dict = System.Collections.Generic.Dictionary<string,obj>()
    match dict with
    | TypeDef (null:Dictionary<_,_>) typeArgs -> printfn "%A" typeArgs
    | _ -> ()
4 голосов
/ 21 июля 2011

Даже если вы используете параметризованный активный шаблон (где аргумент является некоторым выражением), компилятор анализирует аргумент как шаблон (в отличие от выражения), поэтому синтаксис более ограничен.

Я думаю, что это по сути та же проблема, что и обсуждаемая здесь: Как передать сложное выражение параметризованному активному шаблону? (Я не уверен насчет фактической реализации компилятора, но спецификация F # говорит, чтоон должен анализироваться как шаблон).

В качестве временного решения вы можете написать любое выражение внутри кавычки, чтобы вы могли сделать это:

let undef<'T> : 'T = Unchecked.defaultof<_>

let (|TypeDef|) (typeExpr:Expr) (value:obj) =
  let typeDef = typeExpr.Type.GetGenericTypeDefinition()
  // ...

let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef <@ undef<Dictionary<_,_>> @> typeArgs -> printfn "%A" typeArgs
| _ -> ()
...