开发者

Concisely creating an IDictionary<_,obj>

Is there a shorter way of creating an IDictionary<_,obj>, possibly without boxing every value? This is what I have.

let values =
  [ "a", box 1
    "b", box "foo"
    "c", box true ]
  |> dict

Dictionary<_,obj>.Add can be called without boxing, but I couldn't figure out a way to use it that's shorter than what I have.

I'm hoping for something other than defining a boxing operator.

EDIT

Based on Brian's su开发者_运维知识库ggestion, here's one way to do it, but it has its own problems.

let values =
  Seq.zip ["a"; "b"; "c"] ([1; "foo"; true] : obj list) |> dict


Here's a solution, following kvb's suggestion (probably the most concise, and clearest, so far):

let inline (=>) a b = a, box b

let values =
  [ "a" => 1
    "b" => "foo"
    "c" => true ]
  |> dict


Here's the slickest thing I was able to whip up. It has more characters than your boxing version, but possibly feels a little less dirty. Note that the ^ is right-associative (it's the string concat operator inherited from ocaml), which lets it work like ::, and it has stronger precedence than ,, which is why the parenthesis are needed around the tuples.

let inline (^+) (x1:'a,x2:'b) (xl:('a*obj) list) = 
    (x1,box x2)::xl

let values =
  ("a", 1) ^+  ("b", "foo") ^+ ("c", true) ^+ []
  |> dict


I had a similar problem in FsSql and I just tucked away boxing in a function:

let inline T (a,b) = a, box b
let values = dict [T("a",1); T("b","foo"); T("c",true)]


Here's another "solution" which is inspired from Brian's suggestion but it uses reflection so there is a time and safety cost.

let unboxPair (pair:obj) =
    let ty = pair.GetType()
    let x = ty.GetProperty("Item1").GetValue(pair,null) :?> string
    let y = ty.GetProperty("Item2").GetValue(pair,null)
    x,y

let unboxPairs (pairs:obj list) =
  pairs |> List.map unboxPair  

let values =
  unboxPairs
    ["a", 1
     "b", "foo"
     "c", true]
  |> dict


A variation of Stephen's idea:

open System
open System.Collections.Generic

type Dictionary<'a,'b> with
  member this.Add([<ParamArray>] args:obj[]) =
    match args.Length with
    | n when n % 2 = 0 ->
      for i in 1..2..(n-1) do
        this.Add(unbox args.[i-1], unbox args.[i])
    | _ -> invalidArg "args" "even number of elements required"

let d = Dictionary<string,obj>()
d.Add(
  "a", 1,
  "b", "foo",
  "c", true
)


Yet another solution, simply define a bunch of overloaded extension members on Dictionary<'a,'b>:

open System.Collections.Generic
type Dictionary<'a,'b> with
    member this.Add(x1,y1,x2,y2) =
        this.Add(x1,y1)
        this.Add(x2,y2)
    member this.Add(x1,y1,x2,y2,x3,y3) =
        this.Add(x1,y1,x2,y2)
        this.Add(x3,y3)
    member this.Add(x1,y1,x2,y2,x3,y3,x4,y4) =
        this.Add(x1,y1,x2,y2,x3,y3)
        this.Add(x4,y4)
    member this.Add(x1,y1,x2,y2,x3,y3,x4,y4,x5,y5) =
        this.Add(x1,y1,x2,y2,x3,y3,x4,y4)
        this.Add(x5,y5)
    member this.Add(x1,y1,x2,y2,x3,y3,x4,y4,x5,y5,x6,y6) =
        this.Add(x1,y1,x2,y2,x3,y3,x4,y4,x5,y5)
        this.Add(x6,y6)
    //etc.

let values = 
    let d = Dictionary<_,obj>()
    d.Add("a", 1, 
          "b", "foo", 
          "c", true)
    d

Of course values here is not immutable like in your question, but I'm sure you could employ the same strategy in that goal.


let v : (string*obj) list = [...]
let values = dict v

Is one way, the type signature on the left of the list literal will auto-upcast each element.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜