2011-03-15 84 views
4

我有一個函數,它可以返回不同類型,我使用歧視工會爲此。我需要的是將具有歧視性的工會中的一種類型轉換爲另一種類型。 另外一些類型的可以是可轉換到所有其他類型(字符串),但一些類型的只能被轉換成字符串(MyCustomType歧視工會類型之間的轉換

爲此,我已經添加構件方法的ConvertToResultType

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並且還在幾個地方的ConvertTo成員函數中。

有人可以提出更好的解決方案,這種類型轉換?

在此先感謝

+0

如果添加更多類型,轉換矩陣爲O(N^2),所以當然有很多變化,這本質上是一個大問題。 – Brian 2011-03-15 19:38:57

+0

@布萊恩。我不確定這是否可以在這裏使用,以及如何以正確的方式實現,但可以使用一些中間類型* MyIntermediateClass *進行轉換,並轉換爲其他類型,如:* Double - > MyIntermediateClass - > MyCustomClass * - 在這種情況下它將是O(2 * N)。 – Vitaliy 2011-03-16 08:32:07

回答

8

有了一個稍微不同的設計,可以利用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功能更好是有爭議的。

+0

謝謝,這是比我最簡單,最清晰的代碼。它也解決了我的錯誤,當我添加一個枚舉類型。覆蓋* IConvertible *接口將幫助轉換爲CLR類型(但我只使用其中的幾個 - 其他將是多餘的)。還打開關於使用一個大* ConvertTo *函數的問題或將多個* ToObject *實現添加到自定義類型 – Vitaliy 2011-03-16 08:27:35

2

你爲什麼想要做類型轉換到開始?辨別聯盟是一種隱藏類型信息的好方法,直到您需要它並將複雜性抽象出來。一般來說,你在一個使用這種類型的函數中有一個匹配語句,然後你只在需要的時候進行強制轉換。

如果您試圖製作某種類型的解析器或語言引擎,那麼您別無選擇,只能定義所有的轉換或至少它們的錯誤狀態。如果你不介意詳細說明爲什麼/你會用什麼,也許我可以建議另一種方法。另外:F#和.NET通常不支持重載類型的重載。

+0

謝謝你的回答。是的,這被解析器使用,它具有可比較的大型語法樹。每個函數的結果取決於子元素的結果(和結果類型)(對於不同的子元素,返回類型可能不同 - 取決於內部操作)。AST中的某些元素需要轉換爲其他類型。我在想一些統一的功能,但是如果我使用30個自定義類,它將會非常大。這就是爲什麼我問的建議 - 可能有人知道一些特殊的算法,一些其他的方式爲這個 – Vitaliy 2011-03-15 15:47:13

+0

儘可能的解決方案,將擴大類型的區分聯合和添加個人* *的ConvertTo方法,每一種類型。例如:更改** |布爾的布爾**到** |布爾類型**的布爾值並將* ConvertTo *添加到新的*布爾類型*類型 – Vitaliy 2011-03-15 15:53:24

+0

我不確定這會有什麼幫助。如果你真的需要'this.ConvertTo',那麼你就不得不咬緊牙關。 Id使用子功能和通配符運算符_來提高可讀性。試圖擺脫'this.ConvertTo'可能會迫使所有這些邏輯進入代碼的其餘部分,並且很可能導致代碼重複。 – gradbot 2011-03-15 16:08:20