Как создать список F #, содержащий объекты с общим суперклассом? - PullRequest
2 голосов
/ 02 июня 2009

У меня есть две функции, горизонтальная и вертикальная, для размещения элементов управления. Они работают так:

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)]

Ответы [ 3 ]

5 голосов
/ 02 июня 2009

Есть несколько способов, в том числе

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()]

animals1: примечание типа при объявлении var, выгрузка каждого элемента

animals2: введите аннотацию в список выражений, выгружая каждый элемент

animals3: явный тип на первом элементе, оставшийся после отката вверх

В будущей версии F # апкст, скорее всего, станет ненужным.

(См. Также http://cs.hubfs.net/forums/thread/9953.aspx, но ничего «нового» там нет.)

2 голосов
/ 09 августа 2009

Если вы согласны пожертвовать безопасностью типа для удобства чтения, вы можете обойти это:

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]

например. внутри вашей вертикальной функции.

1 голос
/ 02 июня 2009

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

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()]

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...