如果type
具有各种属性,那么当属性为array
,list
或seq
时,如何提取属性类型?
以下示例代码
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>
List<String>
如何更新extractTypeInfo
以便结果具有以下信息
String
Car
String
String
String
答案 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>
的结果中返回Some
或None
以及'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的回答提供了一个很好的工作解决方案,但与往常一样反思,你应该非常清楚你真正想要看到什么以及你真正关注的是什么。
我觉得这里有两点值得注意:
typ.IsGenericType
等于false
,但typ.IsArray
等于true
,您可以使用typ.GetElementType()
获取类型参数。这是出于历史原因,因为反射API早于.NET泛型。IEnumerable<'a>
接口实现的泛型类型参数 - 仅在此处。这意味着虽然类型和接口的泛型类型参数是相同的,但如果你只考虑列表,seqs和数组,在一般情况下它不是给定的。您可以使用与其IEnumerable<'a>
实现不同的类型参数实例化泛型类型(这是地图和字典的情况),或者IEnumerable<'a>
的多个实现(在这种情况下,此解决方案将获取它找到的第一个。)这些可能不是您可能立即发现对您的解决方案很重要的事情,但您可能会及时发现它们。