сопоставление с образцом f# с типами

Я пытаюсь рекурсивно распечатать все свойства объектов и свойства подтипа и т.д. Моя объектная модель следующие...

type suggestedFooWidget = {
    value: float ; 
    hasIncreasedSinceLastPeriod: bool ;
}

type firmIdentifier = {
    firmId: int ;
    firmName: string ;
}
type authorIdentifier = {
    authorId: int ;
    authorName: string ;
    firm: firmIdentifier ;
}

type denormalizedSuggestedFooWidgets = {
    id: int ; 
    ticker: string ;
    direction: string ;
    author: authorIdentifier ;
    totalAbsoluteWidget: suggestedFooWidget ;
    totalSectorWidget: suggestedFooWidget ;
    totalExchangeWidget: suggestedFooWidget ;
    todaysAbsoluteWidget: suggestedFooWidget ;
    msdAbsoluteWidget: suggestedFooWidget ;
    msdSectorWidget: suggestedFooWidget ;
    msdExchangeWidget: suggestedFooWidget ;
}

И моя рекурсия основана на следующем сопоставлении с образцом...

let rec printObj (o : obj) (sb : StringBuilder) (depth : int) 
    let props = o.GetType().GetProperties()
    let enumer = props.GetEnumerator()
    while enumer.MoveNext() do
        let currObj = (enumer.Current : obj)
        ignore <|
             match currObj with
             | :? string as s -> sb.Append(s.ToString())
             | :? bool as c -> sb.Append(c.ToString())
             | :? int as i -> sb.Append(i.ToString())
             | :? float as i -> sb.Append(i.ToString())
             | _ ->  printObj currObj sb (depth + 1)
    sb

В отладчике я вижу, что currObj имеет строку типа, интервал, плавание, и т.д. но это всегда переходит к defualt случаю внизу. Какая-либо идея, почему это происходит?

12
задан PhilBrown 7 June 2010 в 18:36
поделиться

5 ответов

Вот как я заставил это работать...

 let getMethod = prop.GetGetMethod()
 let value = getMethod.Invoke(o, Array.empty)
     ignore <|
         match value with
         | :? float as f -> sb.Append(f.ToString() + ", ") |> ignore
                            ...
6
ответ дан 2 December 2019 в 06:44
поделиться

В вашем примере enumer.Current - это объект, содержащий PropertyInfo. Это означает, что currObj всегда является объектом PropertyInfo и всегда будет соответствовать последнему регистру в вашем операторе сопоставления.

Поскольку вас интересует тип значения свойства, вам необходимо вызвать метод GetValue () PropertyInfo, чтобы получить фактическое значение свойства (как в Ответ ChaosPandion).

Поскольку Enumerator возвращает свои значения как объекты, вам также необходимо преобразовать enum.current в PropertyInfo, прежде чем вы сможете получить доступ к GetValue.

Попробуйте заменить

let currObj = (enumer.Current : obj)

на

let currObj = unbox<PropertyInfo>(enumer.Current).GetValue (o, null)

С этим изменением я могу заставить ваш код работать (в FSI):

>  let test = {authorId = 42; authorName = "Adams"; firm = {firmId = 1; firmName = "GloboCorp inc."} };;
> string <| printObj test (new StringBuilder()) 1;;
val it : string = "42Adams1GloboCorp inc."
1
ответ дан 2 December 2019 в 06:44
поделиться

Как уже отмечали другие, вам нужно вызвать член GetValue, чтобы получить значение свойства - реализованная вами итерация перебирает объекты PropertyInfo, которые являются "дескрипторами свойства", а не фактическими значениями. Однако я не совсем понимаю, почему вы используете GetEnumerator и while цикл в явном виде, когда то же самое можно написать с помощью for цикла.

Также не нужно игнорировать значение, возвращаемое вызовом sb.Append - вы можете просто вернуть его как общий результат (потому что это the StringBuilder). Это фактически сделает код более эффективным (поскольку позволяет оптимизировать хвостовые вызовы). И последнее, вам не нужен ToString в sb.Append(...), потому что метод Append перегружен и работает для всех стандартных типов.

Таким образом, после некоторых упрощений вы можете получить что-то вроде этого (на самом деле здесь не используется параметр depth, но я предполагаю, что вы захотите использовать его для чего-то позже):

let rec printObj (o : obj) (sb : StringBuilder) (depth : int) =
  let props = o.GetType().GetProperties() 
  for propInfo in props do
    let propValue = propInfo.GetValue(o, null)
    match propValue with 
    | :? string as s -> sb.Append(s) 
    | :? bool as c -> sb.Append(c) 
    | :? int as i -> sb.Append(i) 
    | :? float as i -> sb.Append(i) 
    | _ ->  printObj currObj sb (depth + 1) 
16
ответ дан 2 December 2019 в 06:44
поделиться

Вместо этого вам нужно что-то вроде этого.

let rec printObj (o : obj) (sb : StringBuilder) (depth : int) 
    let props = o.GetType().GetProperties() :> IEnumerable<PropertyInfo>
    let enumer = props.GetEnumerator()
    while enumer.MoveNext() do
        let currObj = (enumer.Current.GetValue (o, null)) :> obj
        ignore <|
             match currObj with
             | :? string as s -> sb.Append(s.ToString())
             | :? bool as c -> sb.Append(c.ToString())
             | :? int as i -> sb.Append(i.ToString())
             | :? float as i -> sb.Append(i.ToString())
             | _ ->  printObj currObj sb (depth + 1)
    sb

Это следует из документации MSDN по классу Array:

В .NET Framework версии 2.0 класс класс Array реализует System.Collections.Generic.IList, System.Collections.Generic.ICollection, и System.Collections.Generic.IEnumerable общие интерфейсы. реализации предоставляются массивам во время выполнения, и поэтому не видны инструментам сборки документации инструментов. В результате, общие интерфейсы не появляются в синтаксисе объявления для класса Array класса, и нет никаких ссылок темы для членов интерфейса, которые доступны только путем приведения массива к общий тип интерфейса (явные реализации интерфейса). Главное о чем следует помнить, когда вы приводите массива к одному из этих интерфейсов является то. что члены, которые добавляют, вставляют или удаляют элементы, бросают NotSupportedException.

0
ответ дан 2 December 2019 в 06:44
поделиться

Вы уверены, что программа работает не так, как ожидалось? Интервалы отладчика не всегда надежны.

0
ответ дан 2 December 2019 в 06:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: