获取序列的基础类型(数组,列表,seq)

时间:2017-02-16 22:46:41

标签: .net f#

如果type具有各种属性,那么当属性为arraylistseq时,如何提取属性类型?

以下示例代码

type Car = { name: string }
type CarStore = 
    { name: string
    cars: Car[]
    names: string list
    others: string seq
    others2: ResizeArray<string> }

let peek x =
    Dump x
    x

let extractTypeInfo name (typ:Type) =
    let isEnumerable = true //todo: true for list, array, seq, etc
    let underlyingType = typ //todo: if isEnumerable = true, get actual type
    isEnumerable, underlyingType

typeof<CarStore>.GetProperties()
|> peek
|> Seq.map (fun p -> extractTypeInfo p.Name p.PropertyType)
|> Dump

执行以上操作会产生以下属性,

  • 名称:String
  • 汽车:IStructuralEquatable[]
  • 姓名:FSharpList<String>
  • 其他人:IEnumerable<String>
  • 其他2:List<String>

如何更新extractTypeInfo以便结果具有以下信息

  • 名称:String
  • 汽车:Car
  • 姓名:String
  • 其他人:String
  • 其他2:String

2 个答案:

答案 0 :(得分:8)

我倾向于做这样的事情:

let extractTypeInfo (typ:Type) =
    typ.GetInterfaces()
    |> Array.tryFind (fun iFace -> 
        iFace.IsGenericType && iFace.GetGenericTypeDefinition() = typedefof<seq<_>>)
    |> Option.map (fun iFace -> iFace.GetGenericArguments().[0]) 

此方法通过在seq<'T>的结果中返回SomeNone以及'T的类型来捕获该类型是否为Some情况下。

FSI中的一些例子:

extractTypeInfo typeof<float array>;;
val it : System.Type option =
  Some
    System.Double ...

extractTypeInfo typeof<string list>;;
val it : System.Type option =
  Some
    System.String ...

extractTypeInfo typeof<int>;;
val it : System.Type option = None

答案 1 :(得分:5)

@TheInnerLight的回答提供了一个很好的工作解决方案,但与往常一样反思,你应该非常清楚你真正想要看到什么以及你真正关注的是什么。

我觉得这里有两点值得注意:

  1. 虽然F#类型系统使数组看起来像正确的泛型类型,但这并不是它们在.NET反射API中的表现。给定数组类型,您将typ.IsGenericType等于false,但typ.IsArray等于true,您可以使用typ.GetElementType()获取类型参数。这是出于历史原因,因为反射API早于.NET泛型。
  2. 您正在查看该类型的IEnumerable<'a>接口实现的泛型类型参数 - 仅在此处。这意味着虽然类型和接口的泛型类型参数是相同的,但如果你只考虑列表,seqs和数组,在一般情况下它不是给定的。您可以使用与其IEnumerable<'a>实现不同的类型参数实例化泛型类型(这是地图和字典的情况),或者IEnumerable<'a>的多个实现(在这种情况下,此解决方案将获取它找到的第一个。)
  3. 这些可能不是您可能立即发现对您的解决方案很重要的事情,但您可能会及时发现它们。