2

我有两个功能,水平和垂直,用于布置控件。他们是这样工作的:

let verticalList = vertical [new TextBlock(Text = "one"); 
                             new TextBlock(Text = "two"); 
                             new TextBlock(Text = "three")]

现在verticalList是一个垂直显示三个文本块的控件:

one
two
three

以下是定义:

let horizontal controls = 
    let wrap = new WrapPanel() in
    List.iter (wrap.Children.Add >> ignore) controls ;
    wrap

let vertical controls = 
    let stack = new StackPanel() in
    List.iter (stack.Children.Add >> ignore) controls ;
    stack

当我组合不同类型时出现问题:

let foo = vertical [new TextBlock(Text = "Title"); vertical items]

这抱怨列表的元素不是同一类型。这是真的,但它们有一个共同的超类型(UIElement)。

我知道我可以使用 :> UIElement 向上转换列表中的两个项目,但这是一个丑陋的解决方案。F# 可以推断出常见的超类型。如果不是,为什么不呢?

如果好看就太好了

vertical [X; Y; Z]

不必成为

vertical [(X :> UIElement); (Y :> UIElement); (Z :> UIElement)]
4

3 回答 3

5

有几种方法,包括

type Animal() = class end
type Cat() =
    inherit Animal()
type Dog() =
    inherit Animal()
let animals1 : list<Animal> = [upcast new Cat(); upcast new Dog()]
let animals2 = ([upcast new Cat(); upcast new Dog()] : list<Animal>)
let animals3 = [(new Cat() :> Animal); upcast new Dog()]

动物1:在 var 声明中的类型注释,向上转换每个元素

动物2:列表表达式上的类型注释,向上转换每个元素

动物3:第一个元素的显式类型,向上转换的休息

在 F# 的未来版本中,向上转换可能变得不必要了。

(另见http://cs.hubfs.net/forums/thread/9953.aspx,但那里没有“新”。)

于 2009-06-01T22:59:14.150 回答
2

如果您同意为了可读性而牺牲类型安全,那么您就是解决方法:

open System
let to_list (tuple: Object) = 
    let rec list_after_index (n: int) = 
        let prop = tuple.GetType().GetMethod("get_Item"+n.ToString())
        match prop with
            | null -> []
            | _ -> prop.Invoke(tuple, [||]) :: list_after_index(n+1)
    match tuple with 
        | :? unit -> []
        | _ when tuple.GetType().FullName.Contains(".Tuple`") -> list_after_index(1)
        | _ -> [tuple]

那么你可以像这样使用它:

> to_list ();;
val it : obj list = []
> to_list (1);;
val it : obj list = [1]
> to_list([], 1, "2", 3.0);;
val it : obj list = [[]; 1; "2"; 3.0]

例如,在您的垂直功能内。

于 2009-08-09T11:59:17.377 回答
1

我更喜欢我之前的答案,但在此基础上,如果您有一个应用程序,您不断创建非同质动物列表,您可以随时执行以下操作:

let Animalize (x:Animal) = x  // define a function to upcast
let animals4 = [ Animalize <| new Cat(); Animalize <| new Dog()]
// or even
let (~++) = Animalize // define a prefix operator to upcast (~ makes it prefix)
let animals5 = [ ++ new Cat(); ++ new Dog()]

最后一个几乎可以肯定是对运算符的滥用,除非您处于一个非常专业的领域并且您经常需要像这样的强制,并且您愿意为了简洁而牺牲固有的可读性。

于 2009-06-02T01:01:58.413 回答