Тонкости инициализации
От: vaa  
Дата: 25.01.22 04:08
Оценка:
пара вопросов.
Person alice = new() { Name = "Alice" };
Person bob;
bob.Name = "Bob";

public struct Person { public string Name; }

В чем разница?
Быть может как C++ будет адрес отличаться (стэк или куча)?
и второй вопрос.
отличается ли метод конструктора класса чем либо от обычного?
Какие-то ограничения имеются быть может?
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re: Тонкости инициализации
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.01.22 04:27
Оценка:
Здравствуйте, 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 свойства.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 25.01.2022 4:28 Sinclair . Предыдущая версия .
Re[2]: Тонкости инициализации
От: vaa  
Дата: 25.01.22 05:14
Оценка:
Здравствуйте, Sinclair, Вы писали:

vaa>>
vaa>>public struct Person { public string Name; }
vaa>>

S>Если исправить ошибку
Я имел ввиду структуру.

S>В конструкторе можно инициализировать readonly поля и init-only свойства.

Т.е. технически это обычный метод, если не считать синтаксических ограничений?
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[3]: Тонкости инициализации
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.01.22 05:43
Оценка:
Здравствуйте, vaa, Вы писали:
S>>Если исправить ошибку
vaa>Я имел ввиду структуру.
Не имеет значения. Definite assignment всё равно потребуется. Почему бы вам не попробовать просто скомпилировать ваш код?

S>>В конструкторе можно инициализировать readonly поля и init-only свойства.

vaa>Т.е. технически это обычный метод, если не считать синтаксических ограничений?
Не очень понятно, что такое "обычный метод". Синтаксических ограничений в нём нет — ну, кроме возможности указать :this(...) или :base(...) в заголовке.
Ограничения, о которых я говорю, они не синтаксические, а семантические.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Тонкости инициализации
От: vaa  
Дата: 25.01.22 06:51
Оценка:
Здравствуйте, 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>Ограничения, о которых я говорю, они не синтаксические, а семантические.
понял
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[5]: Тонкости инициализации
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.01.22 10:42
Оценка:
Здравствуйте, 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>понял
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: Тонкости инициализации
От: vaa  
Дата: 26.01.22 01:34
Оценка:
Здравствуйте, Sinclair, Вы писали:

S> Интересно, только что у меня такой пример не компилировался в dotnetfiddle.

здесь
Автор: Sinclair
Дата: 25.01.22
? так вы же там Person как class объявили.
Вроде все компилится
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[7]: Тонкости инициализации
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.01.22 03:08
Оценка: +1 :)
Здравствуйте, vaa, Вы писали:
vaa>Вроде все компилится
Да, похоже недоисправлял. Проклятый ковид.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Тонкости инициализации
От: _NN_ www.nemerleweb.com
Дата: 26.01.22 14:06
Оценка: 3 (1)
Здравствуйте, vaa, Вы писали:

Если мы в C# 10, то разница небольшая есть если определён конструктор без параметров: Parameterless struct constructor

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();        
    }
}


Enter F
A.ctor
Exit F
Enter G
Exit G



https://dotnetfiddle.net/Widget/ZctaeR
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[6]: Тонкости инициализации
От: Mystic Artifact  
Дата: 26.01.22 20:46
Оценка: 2 (1)
Здравствуйте, Sinclair, Вы писали:

Вот вам идея на двоих: нормальный код должен работать в "SkipLocalsInit", когда вот эти переменные на стэке физически не инициализируются, прям как в старые добрые времена.

Поэтому, мне неясно что там курит стандарт на самом деле.

На практике, это выглядит как:

_ = TryGetValue(out var x);
return x == 0; // привет, не проверили результат


Это особенно важно работая с нативным кодом, который не имеет привычки писать в выход, если нечего выводить. А соответственно, мы и не должны это читать.

В общем, теперь такое доступно в C# официально. И более того — любой уважающий себя человек (разрабатывающий библиотеку) — просто обязан это использовать (если крохи перфоманса — фактор). Это вроде крохи, но на stackalloc они совсем не крохи. Впрочем и выделять буфера стоит неинициализированными. ОС и так старается чистить нулями страницу, отдавая ее в пользование, а дальше простите, думайте сами (и дотнет по прежнему сугубо нелогичен в этом — BufferWriter идет на помойку первым же, именно поэтому, хотя это единственный его недостаток).

PS: Именно в рамках C# SkipLocalsInit — ничего не меняет (по крайней мере так заявляется). Активируется просто атрибутом:
[module: System.Runtime.CompilerServices.SkipLocalsInit]
. Тем не менее, это критично понимать, что компилятор перестанет генерировать очистки — в конце концов, out параметры часто выглядят неуклюже именно поэтому, и приходится использовать ref => а это именно путь наступить на предусмотрительно расставленные грабли. Я сам воочию наблюдал именно насколько разительно меняется поведение в pinvoke, когда неправльный код проявляет себя, но, если остаться в рамках именно C# — не уверен, насколько это важно. С практической точки зрения — думаю 99.9% осмысленного кода может это взять, поиметь бенефиты, и будет работать без каких-либо последствий.
Отредактировано 26.01.2022 22:35 Mystic Artifact . Предыдущая версия . Еще …
Отредактировано 26.01.2022 22:34 Mystic Artifact . Предыдущая версия .
Отредактировано 26.01.2022 20:54 Mystic Artifact . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.