#c# #syntax #f# #object-construction
#c# #синтаксис #f# #построение объекта
Вопрос:
В C # можно создавать деревья объектов в довольно сжатом синтаксисе:
var button = new Button() { Content = "Foo" };
Есть ли идиоматический способ сделать что-то подобное в F #?
Записи имеют хороший синтаксис:
let button = { Content = "Foo" }
Насколько я могу судить, построение объекта — это совсем другое дело. Обычно я бы написал такой код, как:
let button = new Button()
button.Content <- "Foo"
Или даже:
let button =
let x = new Button()
x.Content <- "Foo"
x
Один из способов решения проблемы — использовать пользовательский оператор fluent composition:
// Helper that makes fluent-style possible
let inline (.amp;) (value : 'T) (init: 'T -> unit) : 'T =
init value
value
let button = new Button() .amp; (fun x -> x.Content <- "Foo")
Существует ли встроенный синтаксис для достижения этого — или другой рекомендуемый подход?
Ответ №1:
F # позволяет устанавливать свойства прямо в вызове конструктора, поэтому я думаю, что это должно сработать для вас:
let button = Button(Content = "Foo")
Комментарии:
1. Это так приятно! Я оставлю вопрос открытым на некоторое время на случай, если есть какие-либо предложения о менее прямых ситуациях, таких как, например, let StackPanel = new StackPanel() .amp; (fun x -> x.Children.Add(…) … ), а затем я закончу с этим в качестве ответа.
2. У нас нет концепции открытия и закрытия в том смысле, который вы описываете. Если мы закрываем вопрос, это потому, что с ним возникла проблема, и тогда больше нельзя добавлять ответы, если они не будут открыты повторно. То, что вы описываете, на самом деле является тем, что вы примете как лучший ответ, и вы можете изменить свое мнение о том, какой ответ является лучшим в любое время.
3. Я должен был написать «принято» или «помечено». Извините за путаницу. 🙂 Также полезно знать, что можно изменить принятый ответ.
Ответ №2:
В C # этот приятный синтаксис называется инициализатором объекта, а затем ()
может быть удален (1). Чтобы изменить объект «встроенным» (в свободном стиле) после его инициализации, мне хотелось бы иметь With()
метод расширения, аналогичный вашему .amp;
operator (2):
var button = new Button { Content = "Foo" }; // (1)
// (2)
public static T With<T>(this T @this, Action<T> update)
{
change(@this);
return @this;
}
var button2 = button.With(x => x.Content = "Bar")
В F #, для тех, кто предпочитает конвейер вместо operator, функция может быть названа tap
(см. RxJS) или tee
(Скотт Влашин здесь):
// f: ('a -> 'b) -> x: 'a -> 'a
let inline tee f x =
f x |> ignore
x
let button =
Button(Content = "Foo")
|> tee (fun x -> x.Color <- Blue)
|> tee (fun x -> x.Content <- "Bar")
Комментарии:
1. Я также написал это с помощью метода расширения в C #. Я просто предпочитаю стиль инфикса «operator» в F #. В большинстве случаев я мог бы теперь избежать использования оператора, учитывая принятый ответ Брайана, но для других случаев это все равно необходимо.