2016-08-02 3 views
8

Итак, это самый модный код, который я когда-либо писал. Но это полезно, что раздражает. Причиной всего повторения является то, что я хочу поддерживать свободный интерфейс. Если я дополненной базовый класс (который, случается View в данном случае), было бы только вернуть экземпляр View, что помешало бы мне сделать что-то вродеКак я могу высушить этот код F #? (Свободный интерфейс)

let label = theme.CreateLabel().WithMargin(new Thickness(5.0)).WithText("Hello") 

, поскольку свойство Label.Text не реализуется View базовый класс.

Итак, вот мой свободный интерфейс. Приготовься. Это уродливо и повторяемо. Но он также работает и удобен в использовании.

Я пропустил очевидный способ сушить его?

module ViewExtensions = 
    let private withTwoWayBinding<'TElement, 'TProperty, 'TViewModel, 'TView when 'TView :> IViewFor<'TViewModel>>(viewModel: 'TViewModel, view: 'TView, viewModelProperty: Expr<'TViewModel -> 'TProperty>, viewProperty: Expr<'TView -> 'TProperty>) (element: 'TElement) = 
     view.Bind(viewModel, ExpressionConversion.toLinq viewModelProperty, ExpressionConversion.toLinq viewProperty) |> ignore 
     element 
    let private withHorizontalOptions<'TElement when 'TElement :> View> options (element: 'TElement) = 
     element.HorizontalOptions <- options 
     element 
    let private withVerticalOptions<'TElement when 'TElement :> View> options (element: 'TElement) = 
     element.VerticalOptions <- options 
     element 
    let private withAlignment<'TElement when 'TElement :> View> horizontalOptions verticalOptions (control: 'TElement) = 
     control |> withHorizontalOptions horizontalOptions |> withVerticalOptions verticalOptions 
    let private withMargin<'TElement when 'TElement :> View> margin (element: 'TElement) = 
     element.Margin <- margin 
     element 
    let private withActions<'TElement> (actions: ('TElement -> unit)[]) (element: 'TElement) = 
     for action in actions do action(element) 
     element 
    type Xamarin.Forms.Entry with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) = withTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Entry -> unit) = this.With([|action|]) 
    type Xamarin.Forms.Grid with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Grid -> unit) = this.With([|action|]) 
    type Xamarin.Forms.StackLayout with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.With(actions) = withActions actions this 
     member this.With(action: StackLayout -> unit) = this.With([|action|]) 
    type Xamarin.Forms.Button with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.WithText(text) = this.Text <- text; this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Button -> unit) = this.With([|action|]) 
    type Xamarin.Forms.Switch with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) = withTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Switch -> unit) = this.With([|action|]) 
    type Xamarin.Forms.Label with 
     member this.WithHorizontalOptions(options) = withHorizontalOptions options this 
     member this.WithVerticalOptions(options) = withHorizontalOptions options this 
     member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this 
     member this.WithMargin(margin) = withMargin margin this 
     member this.WithText(text) = this.Text <- text; this 
     member this.With(actions) = withActions actions this 
     member this.With(action: Label -> unit) = this.With([|action|]) 

UPDATE

Так, благодаря вашей помощи, ответ да, я что-то очевидное отсутствует. Как пояснил TheQuickBrownFox, если я изменю свободно интерфейс к чему-то формы

let label = theme.CreateLabel() |> withMargin(new Thickness(5.0)) |> withContent("Hello") 

то монстр вы видите выше, можно заменить во всей своей полноте

module ViewExtensions = 
    let withTwoWayBinding<'TElement, 'TProperty, 'TViewModel, 'TView when 'TView :> IViewFor<'TViewModel>>(viewModel: 'TViewModel, view: 'TView, viewModelProperty: Expr<'TViewModel -> 'TProperty>, viewProperty: Expr<'TView -> 'TProperty>) (element: 'TElement) = 
     view.Bind(viewModel, ExpressionConversion.toLinq viewModelProperty, ExpressionConversion.toLinq viewProperty) |> ignore 
     element 
    let withHorizontalOptions options (element: #View) = element.HorizontalOptions <- options; element 
    let withVerticalOptions options (element: #View) = element.VerticalOptions <- options; element 
    let withAlignment horizontalOptions verticalOptions element = element |> withHorizontalOptions horizontalOptions |> withVerticalOptions verticalOptions 
    let withMargin margin (element: #View) = element.Margin <- margin; element 
    let withCaption text (element: #Button) = element.Text <- text; element 
    let withText text (element: #Entry) = element.Text <- text; element 
    let withContent text (element: #Label) = element.Text <- text; element 
    let withSetUpActions<'TElement> (actions: ('TElement -> unit)[]) (element: 'TElement) = (for action in actions do action(element)); element 
    let withSetUpAction<'TElement> (action: 'TElement -> unit) = withSetUpActions([|action|]) 

Это удаление кода очень приятно на самом деле.

+2

Вы не можете сделать постфиксное приложение (иначе «труба вперед») вместо методов расширения? Таким образом, вы можете создавать общие функции с ограничениями. –

+0

Должен признаться, я не совсем понял, что вы имели в виду, пока я не прочитал ответ TheQuickBrownFox. Теперь это имеет смысл. И это работает очень хорошо. Обновление ожидается. –

ответ

11

идиоматический F # подход к беглым интерфейсам просто использовать форвард оператор трубы |>

module ViewHelpers 
    let withMargin margin element = ... 
    let withText text element = ... 

open ViewHelpers 

let label = theme.CreateLabel() |> withMargin (new Thickness(5.0)) |> withText "Hello" 

Я думаю, вы также можете сократить свои подписи функций с помощью flexible types:

let withMargin margin (element: #View) = ... 
+1

Звучит здорово. Я обойдусь, чтобы попробовать как можно скорее, и пометить это как ответ, если все будет хорошо. Как это часто случается с F #, я все время учусь. Гибкие типы - блестящая идея, и я до сих пор не слышал о них. –

Смежные вопросы