Person alice = new() { Name = "Alice" };
Person bob;
bob.Name = "Bob";
public struct Person { public string Name; }
В чем разница?
Быть может как C++ будет адрес отличаться (стэк или куча)?
и второй вопрос.
отличается ли метод конструктора класса чем либо от обычного?
Какие-то ограничения имеются быть может?
Здравствуйте, vaa, Вы писали:
vaa>пара вопросов. vaa>
vaa>Person alice = new() { Name = "Alice" };
vaa>Person bob;
vaa>bob.Name = "Bob";
vaa>public struct Person { public string Name; }
vaa>
vaa>В чем разница?
Если исправить ошибку, добавив Person bob = new();, то в том, что если сеттер Name бросит исключение, то в alice останется null, а bob получит адрес недоконструированного объекта. Это может быть важно, если речь идёт не о локальных переменных, а о полях объекта, видимого за пределами текущего стека.
И еще — в том, что если Name является init-only, то второй способ просто не скомпилируется.
Без исправления приведённый код просто не скомпилируется из-за отсутствия definite assignment.
См. https://dotnetfiddle.net/UB2e6X vaa>Быть может как C++ будет адрес отличаться (стэк или куча)?
нет. vaa>и второй вопрос. vaa>отличается ли метод конструктора класса чем либо от обычного?
да, отличается. vaa>Какие-то ограничения имеются быть может?
В конструкторе можно инициализировать readonly поля и init-only свойства.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
vaa>>public struct Person { public string Name; }
vaa>>
S>Если исправить ошибку
Я имел ввиду структуру.
S>В конструкторе можно инициализировать readonly поля и init-only свойства.
Т.е. технически это обычный метод, если не считать синтаксических ограничений?
Здравствуйте, vaa, Вы писали: S>>Если исправить ошибку vaa>Я имел ввиду структуру.
Не имеет значения. Definite assignment всё равно потребуется. Почему бы вам не попробовать просто скомпилировать ваш код?
S>>В конструкторе можно инициализировать readonly поля и init-only свойства. vaa>Т.е. технически это обычный метод, если не считать синтаксических ограничений?
Не очень понятно, что такое "обычный метод". Синтаксических ограничений в нём нет — ну, кроме возможности указать :this(...) или :base(...) в заголовке.
Ограничения, о которых я говорю, они не синтаксические, а семантические.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Не имеет значения. Definite assignment всё равно потребуется. Почему бы вам не попробовать просто скомпилировать ваш код?
Компилится, я сразу это проверил(dotnet core 6.0):
Person alice = new() { Name = "Alice" };
Person bob;
bob.Name = "Bob";
Person mary;
public struct Person { public string Name; }
вот такой ILSpy:
.method private hidebysig static
void '<Main>$' (
string[] args
) cil managed
{
// Method begins at RVA 0x2084
// Code size 35 (0x23).maxstack 2
.entrypoint
.locals init (
[0] valuetype Person alice,
[1] valuetype Person bob,
[2] valuetype Person mary,
[3] valuetype Person
)
// {
IL_0000: ldloca.s 3
// Person person = default(Person);
IL_0002: initobj Person
// person.Name = "Alice";
IL_0008: ldloca.s 3
IL_000a: ldstr"Alice"
IL_000f: stfld string Person::Name
// Person person2 = person;
IL_0014: ldloc.3
IL_0015: stloc.0// person3.Name = "Bob";
IL_0016: ldloca.s 1
IL_0018: ldstr"Bob"
IL_001d: stfld string Person::Name
// }
IL_0022: ret
} // end of method Program::'<Main>$'
S>Не очень понятно, что такое "обычный метод".
который не требует ключевого слова new(или это оператор?). S>Ограничения, о которых я говорю, они не синтаксические, а семантические.
понял
Здравствуйте, vaa, Вы писали:
vaa>Здравствуйте, Sinclair, Вы писали:
S>>Не имеет значения. Definite assignment всё равно потребуется. Почему бы вам не попробовать просто скомпилировать ваш код? vaa>Компилится, я сразу это проверил(dotnet core 6.0): vaa>
vaa>Person alice = new() { Name = "Alice" };
vaa>Person bob;
vaa>bob.Name = "Bob";
vaa>Person mary;
vaa>public struct Person { public string Name; }
vaa>
Да, вы правы — тонкая оговорка в стандарте: member access не требует definite assignment для структур. Интересно, только что у меня такой пример не компилировался в dotnetfiddle.
vaa>который не требует ключевого слова new(или это оператор?).
Это неверный способ думать о конструкторах. Внутри CLR конструктор — это обычный метод.
В рамках языка конструктор — это очень особенный метод, т.к. его нельзя вызвать напрямую.
Дело не в том, что "конструктор требует ключевого слова new" — вызвать (без reflection) конструктор на существующем объекте не получится хоть со словом new, хоть без.
Есть различные виды object initialization expression, которые подразумевают вызов конструктора. В некоторых из них есть ключевое слово new, в некоторых — нету.
Можно спровоцировать вызов конструктора перед другим конструктором — при помощи тех самых :this(...) и :base(...).
S>>Ограничения, о которых я говорю, они не синтаксические, а семантические. vaa>понял
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
using System;
using static System.Console;
public struct A
{
public A() { I = 2; WriteLine("A.ctor"); }
public int I;
}
public class C
{
static A F()
{
WriteLine("Enter F");
A a = new() { I = 3 };
WriteLine("Exit F");
return a;
}
static A G()
{
WriteLine("Enter G");
A a;
a.I = 1;
WriteLine("Exit G");
return a;
}
public static void Main()
{
_ = F();
_ = G();
}
}
Вот вам идея на двоих: нормальный код должен работать в "SkipLocalsInit", когда вот эти переменные на стэке физически не инициализируются, прям как в старые добрые времена.
Поэтому, мне неясно что там курит стандарт на самом деле.
На практике, это выглядит как:
_ = TryGetValue(out var x);
return x == 0; // привет, не проверили результат
Это особенно важно работая с нативным кодом, который не имеет привычки писать в выход, если нечего выводить. А соответственно, мы и не должны это читать.
В общем, теперь такое доступно в C# официально. И более того — любой уважающий себя человек (разрабатывающий библиотеку) — просто обязан это использовать (если крохи перфоманса — фактор). Это вроде крохи, но на stackalloc они совсем не крохи. Впрочем и выделять буфера стоит неинициализированными. ОС и так старается чистить нулями страницу, отдавая ее в пользование, а дальше простите, думайте сами (и дотнет по прежнему сугубо нелогичен в этом — BufferWriter идет на помойку первым же, именно поэтому, хотя это единственный его недостаток).
PS: Именно в рамках C# SkipLocalsInit — ничего не меняет (по крайней мере так заявляется). Активируется просто атрибутом:
. Тем не менее, это критично понимать, что компилятор перестанет генерировать очистки — в конце концов, out параметры часто выглядят неуклюже именно поэтому, и приходится использовать ref => а это именно путь наступить на предусмотрительно расставленные грабли. Я сам воочию наблюдал именно насколько разительно меняется поведение в pinvoke, когда неправльный код проявляет себя, но, если остаться в рамках именно C# — не уверен, насколько это важно. С практической точки зрения — думаю 99.9% осмысленного кода может это взять, поиметь бенефиты, и будет работать без каких-либо последствий.