Преобразование между типами в дискриминационных профсоюзах

StackOverflow https://stackoverflow.com/questions/5312182

Вопрос

У меня есть функция, которая может возвращать разные типы, и я использую дискриминационный союз для этого. Что мне нужно, так это иметь преобразование из одного типа в дискриминационном союзе в другой тип. Также некоторые из типов могут быть преобразованы ко всем другим типам (Нить), но некоторые типы могут быть преобразованы только в строку (Mycustomtype)

Для этого я добавил метод участника Преобразовать в в ResultType:

type MyTypes = 
   | Boolean       = 1
   | Integer       = 2
   | Decimal       = 3
   | Double        = 4
   | String        = 5
   | MyCustomType  = 6

type ResultType = 
   | Boolean of bool
   | Integer of int
   | Decimal of decimal
   | Double of double
   | String of string
   | MyCustomType of MyCustomType

   with 
     member this.ConvertTo(newType: MyTypes) = 
       match this with 
       | ResultType.Boolean(value) -> 
           match newType with 
           | MyTypes.Boolean -> 
              this
           | MyTypes.Integer -> 
              ResultType.Integer(if value then 1 else 0)
          ...
       | ResultType.MyCustomType(value) -> 
           match newType with 
           | MyTypes.MyCustomType -> 
              this
           | MyTypes.String -> 
              ResultType.String(value.ToString()) 
           | _ -> 
              failwithf "Conversion from MyCustomType to %s is not supported" (newType.ToString())

Мне не нравится такая конструкция, потому что, если я добавлю больше типов, это требует от меня много изменений: Mytypes, ResultType а также в нескольких местах в Преобразовать в функция члена.

Кто -нибудь может предложить лучшее решение для преобразования таких типов?

заранее спасибо

Это было полезно?

Решение

С немного другим дизайном можно использовать System.Convert.ChangeType и тот факт, что конструкторы дискриминированных профсоюзов на самом деле являются функциями:

// statically typed wrapper for System.Convert.ChangeType
let conv a : 'T = System.Convert.ChangeType(a, typeof<'T>) :?> 'T

type MyCustomType() = class end

type ResultType = 
  | Boolean of bool
  | Integer of int
  | Decimal of decimal
  | Double of double
  | String of string
  | MyCustomType of MyCustomType
  with
    member this.ConvertTo (newType:'T->ResultType) =
      match this with
      | Boolean b -> newType( conv b )
      | Integer i -> newType( conv i )
      | Decimal d -> newType( conv d )
      | Double d -> newType( conv d )
      | String s -> newType( conv s )
      | MyCustomType m ->
         if typeof<'T> <> typeof<string> then
            raise (new System.InvalidCastException("MyCustomType can only be converted to String"))
         else
            String (m.ToString())

let i = Integer 42

let b = i.ConvertTo Boolean
printfn "%A" b

let d = i.ConvertTo Decimal
printfn "%A" d

let d2 = i.ConvertTo Double
printfn "%A" d2

let s = i.ConvertTo String
printfn "%A" s

//let mi = i.ConvertTo MyCustomType  // throws InvalidCastException

let m = MyCustomType (new MyCustomType())
let sm = m.ConvertTo String
printfn "%A" sm

//let im = m.ConvertTo Integer // throws InvalidCastException

РЕДАКТИРОВАТЬ: Как только вы добавите больше пользовательских типов, это не очень поможет.

Может быть, вам следует заставить свои пользовательские типы реализовать IConvertible. Анкет Тогда вы можете удалить специальный код корпуса из ConvertTo и полностью полагаться на System.Convert.ChangeType.

Вам все равно придется расширить каждый пользовательский тип ToObject Реализация всякий раз, когда вы добавляете новый пользовательский тип. Действительно ли это лучше, чем центральный ConvertToФункция спорна.

Другие советы

Почему вы хотите сделать преобразование типа с самого начала? Дискриминационные профсоюзы - это хороший способ скрыть информацию типа, пока она вам не понадобится, и абстрактная сложность. Как правило, у вас есть оператор соответствия в функции, которая потребляет этот тип, а затем вы бросаете, только если вам нужно.

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

В стороне: F# и .net в целом не поддерживают перегрузку типов возврата.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top