Метод именования
Свободные интерфейсы обеспечивают удобочитаемость, если имена методов выбираются разумно.
Имея это в виду, я бы хотел назначить этот конкретный API как "антибеглый":
System.Type.IsInstanceOfType
Это член System.Type
, он принимает объект и возвращает true, если объект является экземпляром типа. К сожалению, вы, естественно, склонны читать это слева направо так:
o.IsInstanceOfType(t); // wrong
Когда на самом деле все наоборот:
t.IsInstanceOfType(o); // right, but counter-intuitive
Но не все методы могут быть названы (или расположены в BCL), чтобы предвидеть, как они могут появиться в «псевдоанглийском» коде, так что это на самом деле не критика. Я просто указываю на другой аспект беглых интерфейсов - выбор имен методов, чтобы вызвать наименьшее удивление.
Инициализаторы объектов
Во многих приведенных здесь примерах единственная причина, по которой используется свободный интерфейс, заключается в том, что несколько свойств вновь выделенного объекта могут быть инициализированы в одном выражении.
Но в C # есть функция языка, которая очень часто делает это ненужным - синтаксис инициализатора объекта:
var myObj = new MyClass
{
SomeProperty = 5,
Another = true,
Complain = str => MessageBox.Show(str),
};
Возможно, это объясняет, почему опытные пользователи C # менее знакомы с термином «свободный интерфейс» для объединения вызовов в одном и том же объекте - это не так часто требуется в C #.
Поскольку свойства могут иметь установщики с ручным кодированием, это возможность вызывать несколько методов для вновь созданного объекта без необходимости заставлять каждый метод возвращать один и тот же объект.
Ограничения:
- Установщик свойств может принимать только один аргумент
- Установщик свойств не может быть универсальным
Мне бы хотелось, чтобы мы могли вызывать методы и подключаться к событиям, а также присваивать их свойствам внутри блока инициализатора объекта.
var myObj = new MyClass
{
SomeProperty = 5,
Another = true,
Complain = str => MessageBox.Show(str),
DoSomething()
Click += (se, ev) => MessageBox.Show("Clicked!"),
};
И почему такой блок модификаций должен применяться только сразу после постройки? Мы могли бы иметь:
myObj with
{
SomeProperty = 5,
Another = true,
Complain = str => MessageBox.Show(str),
DoSomething(),
Click += (se, ev) => MessageBox.Show("Clicked!"),
}
* * * * * * * * * * * * * * * * * * * * * * with
* * * * * * * * * * * *1049* * * * * * * * * * * * * * * * * * * * * *1049* будет новым ключевым словом, которое работает с объектом некоторого типа и производит тот же объект и тип - обратите внимание, что это будет выражение , а не выражение . Так что это точно отражает идею объединения в «свободный интерфейс».
Таким образом, вы можете использовать синтаксис в стиле инициализатора независимо от того, получили ли вы объект из выражения new
или из метода IOC или фабричного метода и т. Д.
На самом деле вы можете использовать with
после полного new
, и это будет эквивалентно текущему стилю инициализатора объекта:
var myObj = new MyClass() with
{
SomeProperty = 5,
Another = true,
Complain = str => MessageBox.Show(str),
DoSomething(),
Click += (se, ev) => MessageBox.Show("Clicked!"),
};
И, как Чарли указывает в комментариях:
public static T With(this T with, Action<T> action)
{
if (with != null)
action(with);
return with;
}
Вышеуказанная оболочка просто заставляет невозвратное действие возвращать что-то, и, эй, presto - в этом смысле все может быть "беглым".
Эквивалент инициализатора, но с включением события:
var myObj = new MyClass().With(w =>
{
w.SomeProperty = 5;
w.Another = true;
w.Click += (se, ev) => MessageBox.Show("Clicked!");
};
И по заводскому методу вместо new
:
var myObj = Factory.Alloc().With(w =>
{
w.SomeProperty = 5;
w.Another = true;
w.Click += (se, ev) => MessageBox.Show("Clicked!");
};
Я не смог удержаться и от проверки на "null" в стиле "возможно, монады", поэтому, если у вас есть что-то, что может вернуть null
, вы все равно можете применить к нему With
и затем проверить его на null
-ness.