Skip to content

Instantly share code, notes, and snippets.

@controlflow
Last active Jan 1, 2016
Embed
What would you like to do?
Primary ctors
class ReverseForLookupItem : ForLookupItemBase
{
public ReverseForLookupItem([NotNull] PrefixExpressionContext context,
[NotNull] LiveTemplatesManager templatesManager,
[CanBeNull] string lengthPropertyName)
: base("forR", context, templatesManager, lengthPropertyName) { }
protected override IForStatement CreateStatement(CSharpElementFactory factory, ICSharpExpression expression)
{
...
}
}
// vs.
class ReverseForLookupItem([NotNull] PrefixExpressionContext context,
[NotNull] LiveTemplatesManager templatesManager,
[CanBeNull] string lengthPropertyName)
: ForLookupItemBase("forR", context, templatesManager, lengthPropertyName)
{
protected override IForStatement CreateStatement(CSharpElementFactory factory, ICSharpExpression expression)
{
...
}
}
@controlflow
Copy link
Author

controlflow commented Dec 21, 2013

По мне - первый вариант читабельнее :)

@ViIvanov
Copy link

ViIvanov commented Dec 21, 2013

Я бы не из читабельности исходил, а из смысла. Например у Point очевидно Point(int, int) есть primary, а тут появится ещё один ctor как решать - какой из primary - которые более или которые более "читабелен"?

Например, у ConcurrentDictionary<,> целая пачка конструкторов (http://msdn.microsoft.com/en-us/library/dd287238.aspx) - какой из них primary? Я бы не смог выбрать и вообще обошёлся обычными конструкторами.

Для Point { public int X { get; } public int Y { get; } } тоже можно придумать множество конструкторов, но там понятно, какой сделать primary.

@controlflow
Copy link
Author

controlflow commented Dec 21, 2013

Вот, уже начинается философия :)

Можно сказать, что конструктор есть смысл сделать primary, если он своими параметрами определяет весь набор состояния объекта и обычно это состояние выставлено через свойства :) Однако в примере выше это не особо и так, а выглядит вроде и вполне нормально...

А вообще, это всего лишь синтаксический сахар, наверное стоит не руководствоваться семантикой объекта, а лишь синтаксическим бенефитом :)

@chaliy
Copy link

chaliy commented Dec 21, 2013

А если сделать ReverseForLookupItem дженериком с тремя парамтрами... ото колбасень будет.

Если серьезно, то у меня очень громадное количество класов с одним конструктором. DI через конструктор. В таких случаях, второй вариант будет читабельней.

@controlflow
Copy link
Author

controlflow commented Dec 21, 2013

Да конечно для DTO и классов-компонентов будет офигенный бенефит, это не может не радовать :)

...но опять же, наверняка будут complain'ы от юзеров, которые захотят отличать состояние класса (филды) от параметров и локальных переменных. Начнутся споры что параметры классов надо называть используя нейминг стайл филдов, или референся их использовать explicit this. Или вообще не использовать primary constructor'ы :))

@controlflow
Copy link
Author

controlflow commented Dec 21, 2013

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

@nesteruk
Copy link

nesteruk commented Dec 22, 2013

К контексте convention over configuration можно будет договориться, что primary ctor - это в который что-то инжектится, тем самым не писать [InjectionConstructor] если их много.

@ViIvanov
Copy link

ViIvanov commented Dec 23, 2013

Если праймари ктор - только сахар, то действительно разницы никакой. Тут надо думать только о том, что несколько странно будет менять этот конструктор, не будет ли брекинг-ченджем изменение этого конструктора. Мне просто казалось это шажок к ПМ (матчинг именно по нему будет делаться), и когда тот наконец появится, вот тогда будет важно, что будет выбрано в каачестве этого конструктора.

@controlflow
Copy link
Author

controlflow commented Dec 29, 2013

@nesteruk кажется невозможно будет отличить primary constructor от любого другого...

@Vilvanov я очень-очень сомневаюсь, что в C# когда-либо доберутся до ПМ, потому что это слишком масштабная фича чтобы уже запихнуть в насквозь мутабельный и императивный язык. Вроде уже говорили, что параметры класса (не знаю как они будут называться в спеке, по-моему логично называть именно параметрами класса как в F#) будут приватными полями (а если не говорили, то я готов поставить пиво что так будет), а значит никакого паттерн-матчинга. Добавлять в язык ПМ, который будет работать только по классам с primary-конструктором - это как-то ущербно, а во все остальное просто так ПМ не протащить. К тому же ПМ по сути "безопасен" только поверх неизменяемых структур данных (чтобы гарантировать отсутствие случаев типа match someType with SomeType(true) when someType.DoSideEffectAndChangeInternalValue() -> ... | SomeType(false) -> ... - казалось бы паттерн полный, но если мутировать структуру, то может не сработать ни один из кейсов).

@ViIvanov
Copy link

ViIvanov commented Dec 31, 2013

Хм, если параметры праймари-конструктора автоматом делаются полями, что смысл в этом вот:

public class Point(int x, int y) {
  public int X { get; } = x;
  // Зачем так, как выше - разве там не создастся ещё одно поле?
  public int Y { get; } = x % 2; // тут ещё одно поле не будет создано?

  // Почему не просто так, как ниже?
  // Понятно, что короче, но ради этого новый синтаксис воротить?
  public int X { get { return x; } }
}

хотя очень логично, конечно же.

А если всё и взаправду так, как ты говоришь, то тем более выбирать праймари ктор по методе "что читабельнее/компактнее/более ёмко" так же не верно. Раз праймари ктор определяет не просто синтаксис создания экземпляра, но и наполнение (список полей) то и выбираться должен не из эстетичеких понятий.

Ну, поживём-увидим :о))

@controlflow
Copy link
Author

controlflow commented Dec 31, 2013

Я не знаю точно, но скорее всего список полей будет определяться как в F# - если параметр класса использован только в вызове базового класса или инициализаторе поля/свойства (если такие будут в C# 6.0), то поле для него создаваться не будет.

class Point(int x, int y, int z) : PointBase(x) { // поля для x создано не будет
  public readonly int FieldY = y + 1; // поля для y тоже не будет создано

  // чтобы параметры классов были более полезными, логично ожидать что
  // в C# 6.0 появятся инициализаторы для автосвойств, аналогичные инициализаторам полей:
  public string MutableAutoPropertyZ { get; set; } = z.ToString(); // поля для z тоже не должно быть

  // про инициализаторы автосвойст не было информации,
  // но то что показали на NDC London об этом свидетельствует:
  public string ReadOnlyAutoPropertyZ { get; } = z; // поля для z по-прежнему не будет

  // это автосвойство без одного акцессора - без сеттера.
  // я сомневаюсь, что им можно будет присваивать в конструкторах
  // (как-то странно выглядело бы, сеттера нет, а присваивать можно),
  // но при этом их можно инициализировать как поле (нету никаких причин
  // не расширить эту фичу на обычные автосвойства). Будет полезно для коллекций:
  public List<string> Tags { get; } = new List<string>();
}

@controlflow
Copy link
Author

controlflow commented Dec 31, 2013

// и да, тут два readonly поля (которые явно написаны):
class Point(int x, int y) {
  public readonly int X = x;
  public readonly int Y = y;
}

// тут тоже два поля (readonly backing-поле автосвойства):
class Point(int x, int y) {
  public int X { get; } = x;
  public int Y { get; } = y;
}

// тут два backing-поля автосвойства (мутабельные):
class Point(int x, int y) {
  public int X { get; set; } = x;
  public int Y { get; set; } = y;
}

// тут два поля, получившиеся из параметров класса (скорее всего мутабельные):
class Point(int x, int y) {
  public int X { get { return x; } }
  public int Y { get { return y; } }
}

// абсолютно то же самое, только короче (показывали на NDC):
class Point(int x, int y) {
  public int X => x;
  public int Y => y;
}

// скорее всего можно будет так (два поля из параметров классов):
class Point(int x, int y) {
  public int X {
    get { return x; }
    set { x = value; }
  }

  public int Y {
    get { return y; }
    set { y = value; }
  }
}

@ViIvanov
Copy link

ViIvanov commented Jan 4, 2014

Кажется, слишком наворочено. Поживём-увидим :о)

@hazzik
Copy link

hazzik commented Apr 8, 2014

@controlflow

Так не работает

class Point(int x, int y) {
  public int X { get { return x; } }
  public int Y { get { return y; } }
}

(7,31): error CS9007: Parameters of a primary constructor can only be accessed in instance variable initializers and arguments to the base constructor.
(8,31): error CS9007: Parameters of a primary constructor can only be accessed in instance variable initializers and arguments to the base constructor.

Последние 3 примера не работают

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment