Имя: Пароль:
1C
 
Гуру-тест для программистов 1С по C#, на сообразительность.
,
0 Гений 1С
 
гуру
15.01.21
13:13
Итак, нужен рефакторинг кода.

Есть много кода, вида:

if (cond1)
{  struct_A r;
   r.field1 = "aга";
   r.field2 = false;
} else
if (cond2)
{  struct_B r;
   r.field1 = "aга";
   r.field2 = false;
} else
{  struct_C r;
   r.field1 = "aга" + "мухоха"; //иногда код отличается для некоторых структур
   r.field2 = false;
}



Я придумал в итоге, как сделать красиво. Жду ваших гипотез.

ООП (классы) не применял - из тушки по воробьям, считаю.
Макросы нельзя в c#, увы.

Но есть одно хитрое решение! ;-)
54 Ненавижу 1С
 
гуру
15.01.21
16:08
(53) ну хорошо, что из того следует?
55 Ненавижу 1С
 
гуру
15.01.21
16:14
(53) ты так написал, что ТОЛЬКО для этого и используются
56 Кирпич
 
15.01.21
16:37
(53) Кому не лень, могут и в гугле перевести. Вы с гением не в одной палате лежите случайно?
57 jbond
 
15.01.21
16:49
(55) - такого мнения мелкософт.

У них же написано: Не используйте struct, кроме специальных случаев ...
58 Serginio1
 
15.01.21
17:31
(57) struct удобны и для скорости и для уменьшения нагрузки на сборщик мусора.
После того как добавили ref return и ref struct
Скорость выполнения приближается к С++

https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/builtin-types/struct#ref-struct
https://docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/classes-and-structs/ref-returns
https://docs.microsoft.com/ru-ru/dotnet/api/system.span-1?view=net-5.0
59 Serginio1
 
15.01.21
17:33
60 ДедМорроз
 
15.01.21
21:27
А зачем вообще рефакторить это ?
Если объекты разные и должны быть разными,то создавать в конструкторе.
61 ДедМорроз
 
15.01.21
21:43
Ну и в Си++ структура - это класс,у которого все поля public.
То есть,на самом деле,особой разницы нет.
Но в компиллируемых языках расточивание методов происходит в момент компилляции.
62 Serginio1
 
15.01.21
22:47
(61) В С++ разницы практически нет. Структура это класс на стеке.
В .Net ограничения на структуру в том, что нет наследования.
Любое приведение к object это боксинг и унбоксинг.
Интерфейсы для структур только с боксингом.
https://blogs.u2u.be/u2u/post/c-value-type-boxing-by-interfaces

В свое время были эксперименты по размещению классов на стеке
https://xoofx.com/blog/2015/10/08/stackalloc-for-class-with-roslyn-and-coreclr/

Но дальше не пошли
63 Кирпич
 
16.01.21
06:41
(62) "Структура это класс на стеке." Вы совсем там трёхнулись на свом ООП? Структура это просто структура. Какая разница, на стеке она или ёё из кучи выделили.
64 Ненавижу 1С
 
гуру
16.01.21
07:16
(61)  а причем здесь C++?
(63)  в производительности, когда приведете к интерфейсу - она уедет в кучу, потом вернется обратно
65 Ненавижу 1С
 
гуру
16.01.21
07:18
(63) ну и в семантике, например, присваивания
66 Serginio1
 
16.01.21
13:07
(63) Ну не правильно высказался. В С++ разницы между классом и структурой нет. Класс может как на стеке так и в памяти.
В C# структура только на стеке или как поле класса. А класс не может создаваться на стеке.
Так понятно?
Структуры нужны для скорости и уменьшения нагрузки на GC.
В приведенном примере https://xoofx.com/blog/2015/10/08/stackalloc-for-class-with-roslyn-and-coreclr/
есть разница

The stack version will run in 400 ms with 0 GC collect
The heap version will run in 5000 ms with 100+ GC collect

Например в Java нет структур и много мест где производительность проседает с большим количеством классов (массивы), по сравнению с value типами
Меньше нагрузка на GC. И память выделяется одним огромным куском.
67 Кирпич
 
16.01.21
14:08
(66) "В C# структура только на стеке или как поле класса."
С какого перепугу только на стеке? Я могу описать структуру и создавать её с помощью new. Разве нет?

using System;

public struct XXX {
        string x;
        bool y;
    }

class HelloWorld {
  static void Main() {
    var x1 = new XXX();
    var x2 = new XXX();
  }
}
68 Кирпич
 
16.01.21
14:12
Можно и так и так

    var x1 = new XXX(); //в куче
    XXX x2;             //на стеке, но не факт
69 Ненавижу 1С
 
гуру
16.01.21
14:16
(67) (68) подучись перед тем как писать такое. new само по себе не значит, что в куче
70 Кирпич
 
16.01.21
14:18
(69) А что же оно значит?
71 Ненавижу 1С
 
гуру
16.01.21
14:31
(70) оно для вызовов конструкторов
кстати, для структур .net поддерживает (в отличие от c#) конструкторы без параметров

но скорее всего в (68) описано одно и тоже
72 Кирпич
 
16.01.21
14:34
(71) А конструкторы где память выделяют? Не на стеке же ёптыть.
73 Ненавижу 1С
 
гуру
16.01.21
14:45
(72) а почему они ее выделяют?
Кстати:

    
    struct A
    {
        public int x;
    }


            A a1 = new A();
            Console.WriteLine(a1.x); //ок

            A a2;
            Console.WriteLine(a2.x); //не ок
74 Кирпич
 
16.01.21
14:51
(73) " Console.WriteLine(a2.x);//не ок"
Ну это примудрости C#. Он не дает печатать мусор из стека. Присвой значение a2.x и прокатит
75 Кирпич
 
16.01.21
14:53
(73) "а почему они ее выделяют?"
Ну как бы под объект то надо выделить память, если он реально создается
76 Кирпич
 
16.01.21
14:58
+(74) В C# стековые надо инициализировать. Даже такое не прокатит

int x;
Console.WriteLine(x);
77 Ненавижу 1С
 
гуру
16.01.21
14:58
(75) она уже выделена статически, как и под любую переменную

int x = 42;

это тоже структура и тоже на стеке
78 Кирпич
 
16.01.21
14:59
(77) Вот ты x инициализировал. А структуру не инициализировал. Потому и ругается.
79 Ненавижу 1С
 
гуру
16.01.21
15:01
(78) я про то, что локальные переменные типа структуры (value object) живут на стеке
80 Кирпич
 
16.01.21
15:03
(79) Если просто объявить, без new, то на стеке. Иначе будут в куче. Как я и писал в (68).
81 Ненавижу 1С
 
гуру
16.01.21
15:06
(80) продолжай в это верить
82 Кирпич
 
16.01.21
15:08
(81) А как по другому? Как правильно?
83 Кирпич
 
16.01.21
15:11
Структуры в C# всегда выделяются на стеке? Это же глупо и смешно.
84 Ненавижу 1С
 
гуру
16.01.21
15:12
(83) не всегда, как члены класса - живут в куче
85 Serginio1
 
16.01.21
15:13
(67) https://docs.microsoft.com/ru-ru/dotnet/standard/design-guidelines/choosing-between-class-and-struct

Первое различие между ссылочными типами и типами значений будет рассмотрено в том, что ссылочные типы выделяются в куче и уничтожаются сборщиком мусора, тогда как типы значений выделяются либо в стеке, либо в виде встроенных строк, содержащих типы, и освобождаются при освобождении стека или при освобождении содержащего их типа. Таким образом, выделение и освобождение типов значений являются общими дешевле, чем выделение и освобождение ссылочных типов.

new для value типов это инициализатор
https://stackoverflow.com/questions/203695/does-using-new-on-a-struct-allocate-it-on-the-heap-or-stack

структуры не могут быть в памяти, только в качестве объекта боксинг.
то есть var box= (object)( new A());

для конструкторов value типов применяется несколько инциализаторов в заваисмости где память выделяется

newobj: Выделяет значение в стеке, вызывает параметризованный конструктор. Используется для промежуточных значений, например для присвоения полю или использования в качестве аргумента метода.

call instance: Использует уже выделенное место хранения (будь то в стеке или нет). Это используется в приведенном выше коде для назначения локальной переменной. Если одной и той же локальной переменной присваивается значение несколько раз с помощью нескольких newвызовов, она просто инициализирует данные поверх старого значения - она не выделяет больше пространства стека каждый раз.

initobj: Использует уже выделенное место хранения и просто стирает данные. Это используется для всех наших вызовов конструктора без параметров, включая те, которые присваиваются локальной переменной. Для вызова метода эффективно вводится промежуточная локальная переменная ,и ее значение стираетсяinitobj.
86 Кирпич
 
16.01.21
15:17
(85) Ну так могут структуры выделятся в куче или нет?
87 Serginio1
 
16.01.21
15:18
(85) >> для конструкторов value типов применяется несколько инциализаторов в заваисмости где память выделяется
Вернее выделение памяти на стеке и инциализация передаваемыми параметрами или обнуление памяти
88 Serginio1
 
16.01.21
15:19
(86) Как структуры нет, как объект да. Но это уже отбоксенные структры приведенные к object
89 Serginio1
 
16.01.21
15:20
90 Кирпич
 
16.01.21
15:21
(88) Не один ли хрен отбоксенные они или нет. Они структуры и они не на стеке.
91 Serginio1
 
16.01.21
15:25
(90) Большая. Это уже объект. У него есть поле со ссылкой на type (vmt).
По сути это уже другой тип, просто он приводится к value типу автоматически.
92 Serginio1
 
16.01.21
15:29
91 верее не автоматически а явное привидение (int) .
Автоматически он к боксенному объекту приводится
93 Кирпич
 
16.01.21
15:31
(91) А по мне так никакой разницы. Мне пофигу что там .NET внутри с моей структурой тварит. Пускай пакует во что хочет. Я пишу через точку и всё. А она в куче.
94 Serginio1
 
16.01.21
15:44
(93) Тебе пофигу. В Java тоже так посчитали. Но у них int и боксенный Integer
https://habr.com/ru/post/104231/
Так, заменив один int на Integer, можно сэкономить около 40% используемой памяти.

Value типы нужны прежде всего для скорости при уменьшении нагрузки на сборщик мусора.
Все зависит от количества таких объектов.
Выбор между классом и структурой
https://docs.microsoft.com/ru-ru/dotnet/standard/design-guidelines/choosing-between-class-and-struct

Если тебя скорость не интересует. То идеальным выбором конечно будет класс.

Про боксинг. Нужно не забывать отбоксить.

interface IBankAccount
{
    void Add(int amount);
}

struct BankAccount : IBankAccount
{
    int value;

    public BankAccount(int value) => this.value = value;
    void IBankAccount.Add(int amount) => value += amount;
    public void Add(int amount) => value += amount;
    public void Print() => Console.WriteLine($"Value: {value}");
}

BankAccount ba = new BankAccount(100);
ba.Add(50);
ba.Print();

IBankAccount iba = ba;
iba.Add(50);
ba.Print();
This piece of code will probably not print out what you might expect... The output is as follows:

Value: 150
Value: 150

Нужно обратно скопировать данные
ba=(BankAccount)iba;
ba.Print();

тогда получим 200
95 Ненавижу 1С
 
гуру
16.01.21
16:18
Интересно автор поста вкуривает разницу между struct и class в c# и c++?
96 Кирпич
 
16.01.21
16:22
(94) Да я не собираюсь нигде структуры использовать. Я на C# не пишу пока.
Интересно, а как такая фигня работает? Получается, в xxx() автоматом боксится в объект и возвращает ссылку на него.

public struct XXX {
   public int x;
   public int y;
}

class HelloWorld {
    
static ref XXX xxx() {
    XXX r;
    return ref r;
}
  static void Main() {
    ref XXX x1 = ref xxx();
    ref XXX x2 = ref x1;
    
    x1.x = 100500;
    
    Console.WriteLine(x2.x);
  }
}
97 Ненавижу 1С
 
гуру
16.01.21
16:34
(96) так нельзя:
static ref XXX xxx() {
    XXX r;
    return ref r;
}

Нельзя передать ссылку на локальную переменную за пределы ее существования
98 Кирпич
 
16.01.21
16:36
(97) Мне можно. У меня работает.
99 Serginio1
 
16.01.21
16:44
(96) Нет не боксится. Это аналог ref return и параметров передаваемых через out ref in
https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/builtin-types/struct#ref-struct
Начиная с C# 7.2 в объявлении типа структуры можно использовать модификатор ref. Экземпляры типа структуры ref выделяются в стеке и не могут временно перейти в управляемую кучу. Для этого компилятор ограничивает использование типов структуры ref следующим образом:
Структура ref не может быть типом элемента массива.
Структура ref не может быть объявленным типом поля класса или структурой, отличной отref.
Структура ref не может реализовывать интерфейсы.
Структура ref не может быть упакована в System.ValueType или System.Object.
Структура ref не может быть аргументом типа.
Переменная структуры ref не может быть зафиксирована лямбда-выражением или локальной функцией.
Переменную структуры ref нельзя использовать в методе async. Однако переменные структуры ref можно использовать в синхронных методах, например в тех, которые возвращают Task или Task<TResult>.
Переменную структуры ref нельзя использовать в итераторах.
100 Serginio1
 
16.01.21
16:45
101 Serginio1
 
16.01.21
16:46
102 Serginio1
 
16.01.21
16:48
Ну и передача параметров по ссылке in,ref,out
https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/keywords/in-parameter-modifier
103 Ненавижу 1С
 
гуру
16.01.21
16:49
(98) Ошибка    CS8168    Невозможно вернуть по ссылке локальный "r", так как это не локальная переменная ref    ConsoleApp10    C:\Users\User\source\repos\ConsoleApp10\ConsoleApp10\Program.cs    15
104 Кирпич
 
16.01.21
16:54
(102) Так а как можно вернуть ссылку на стековую переменную. Это же глюк. Получается боксится
105 Кирпич
 
16.01.21
16:54
(103) Чота не правильно написал наверное
106 Кирпич
 
16.01.21
16:55
или С# старый
107 Ненавижу 1С
 
гуру
16.01.21
16:57
(105) я копировал, а не писал. Шарп новый
108 Serginio1
 
16.01.21
16:58
(103) ref struct только для .Net Standard 2.1 .То есть только для Core. Для .Net Framework не доступен.
109 Serginio1
 
16.01.21
16:59
110 Кирпич
 
16.01.21
17:00
(103) Извини, братан. Я просто здесь пробую. Хрен знает чо там за C# Ж))
https://www.onlinegdb.com/online_csharp_compiler
111 Кирпич
 
16.01.21
17:04
(108) Так ссылка на что здесь возвращается? На стековую переменную или таки создается копия этой переменной в куче и уже на нее возвращется ссылка?


static ref XXX xxx() {
    XXX r;
    return ref r;
}
112 Кирпич
 
16.01.21
17:04
А ну щас несколько раз вызову
113 Serginio1
 
16.01.21
17:07
(111) А как ты думаешь? Что такое передача по ссылке?
ref, out, in

Method(ref int param);

Для проверки присвой разным переменным и измени
114 Serginio1
 
16.01.21
17:08
и посмотри какое значение в реальном объекте
115 Ненавижу 1С
 
гуру
16.01.21
17:09
(110) это не настоящий: Mono 5.10.1.20 (tarball Thu Mar 29 10:48:35 UTC 2018)
116 Кирпич
 
16.01.21
17:10
(115) Ааааа. Так может это просто баг у них такой
117 Кирпич
 
16.01.21
17:12
(114) Я знаю что такое по ссылке. Меня смущает, что ссылка на стековую переменную, которая станет мусором при выходе из xxx()
118 Serginio1
 
16.01.21
17:12
(115) Попробуй на .Net 5
119 Ненавижу 1С
 
гуру
16.01.21
17:16
(118) я и попробовал на нем:

static ref XXX xxx() {
    XXX r;
    return ref r; //<<вот так нельзя локальную переменную вернуть CS8168    
}
120 Кирпич
 
16.01.21
17:18
Интересно это баг в Mono или у них там реально боксит
121 Serginio1
 
16.01.21
18:03
https://docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/classes-and-structs/ref-returns

Для классса прокатывает
public  class TestRef
    {
        int i;

        public ref int RefProperty { get { return ref i; } }

        public ref int GetRefProperty()
        {
            return ref i;
        }
    }

Для struct чеhез Span

public ref struct TestRefStruct
    {
        int i;
        //var array = new byte[100];
        Span<byte> arraySpan;// = new Span<byte>(array);
        public ref byte RefProperty { get { return ref arraySpan[0]; } }

      
    }

или расширения
https://overcoder.net/q/495043/почему-метод-структуры-c-не-может-вернуть-ссылку-на-поле-а-метод-не-являющийся
122 Serginio1
 
16.01.21
18:13
Из комментариев
Документация» ( github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/… ) гласит, что «поля структуры экземпляра безопасны для возврата до тех пор, пока получатель безопасен для возврата». Здесь приёмник (параметр foo ) безопасен для возврата (потому что in ), поэтому, кажется, работает как задумано. В то время как первый случай покрыт "this", небезопасно возвращаться из членов структуры ".
123 Кирпич
 
17.01.21
08:48
Можно структуру в массив запихнуть.

        static ref XXX xxx()
        {
            XXX[] x = { new XXX() };
            return ref x[0];
        }

Получается почти как в Mono

static ref XXX xxx() {
    XXX r;
    return ref r;
}
124 Гений 1С
 
гуру
17.01.21
09:08
Все бы решилось если бы в C# были бы генераторы статического кода, то бишь макросы, как в Си, было бы намного красивее и без извращений:

MULTICOND
  Cond1 {struct_A r;}  
  Cond2 {struct_B r;})
GENERAL
{
   r.field1 = "aга";
   r.field2 = false;
}
125 Кирпич
 
17.01.21
09:15
(124) А почему нельзя просто функцию написать и копипастить её вызов?
126 Ненавижу 1С
 
гуру
17.01.21
10:37
(124) макросы это зло
Генераторы кода есть
127 Гений 1С
 
гуру
17.01.21
10:46
(126) зло не зло, а тут красивее через генераторы кода (макросы).
(125) потому что типы разные.
128 Ненавижу 1С
 
гуру
17.01.21
10:53
(127) правильнее через интерфейсы
129 Конструктор1С
 
17.01.21
10:53
130 Конструктор1С
 
17.01.21
10:55
131 Ненавижу 1С
 
гуру
17.01.21
10:57
(127) нужна статья про кризис-айти в виде отсутствия макросов в шарпе
132 Кирпич
 
17.01.21
11:02
(127) "типы разные"
ну ты тип как параметр тоже используй
133 Гений 1С
 
гуру
17.01.21
11:09
(132) нельзя, язык не поддерживает
134 Ненавижу 1С
 
гуру
17.01.21
11:11
(133) передавать как параметр так себе идея. Но тип Type там есть из коробки

Рано тебе ещё гуру-тесты создавать
135 Гений 1С
 
гуру
17.01.21
11:12
(134) и как Type поможет?
136 Ненавижу 1С
 
гуру
17.01.21
11:14
(135) тебе никак. Забудь. Я кстати за адаптер и интерфейс, а не интроспекцию
137 Гений 1С
 
гуру
17.01.21
11:18
(136) вот видишь, макрос проще решает эту проблему
138 Кирпич
 
17.01.21
11:27
(137) покажи макрос для С++, который решит такую задачу
139 Ненавижу 1С
 
гуру
17.01.21
11:30
(137) не вижу. Костыли это
140 Serginio1
 
17.01.21
11:37
(123) Да только XXX[] x = { new XXX() }; это уже объект. Не то
(124) Ты не поверишь, но уже есть
Introducing C# Source Generators
https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/
https://blog.jetbrains.com/dotnet/2020/11/12/source-generators-in-net-5-with-resharper/
https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.md
141 Кирпич
 
17.01.21
11:39
(140) Массив объект? Ну наверное. А структура в массиве не объект же?
142 Ненавижу 1С
 
гуру
17.01.21
11:42
(141) массив это точно объект. Класс Array
Структура нет. Мне интересно подсчет таких ссылок тоже используется, чтобы объект раньше времени не финализировался?
143 Кирпич
 
17.01.21
11:43

static ref XXX xxx()
   {
       return ref (new XXX[1])[0];
   }


Так вот еще можно
144 Ненавижу 1С
 
гуру
17.01.21
11:47
(143) ты уже проверяешь на тру-компиляторе?
145 Кирпич
 
17.01.21
11:48
(144) ага
146 Serginio1
 
17.01.21
11:53
(143) new XXX[1] это массив. Он выделяется в куче. С этим проблем нет.
Проблема в структурах выделяемых на стеке. При этом ref держит ссылку которая уже не валидная, из-за перезаписи стека.
С объектами такой проблемы нет ибо за объектом следит GC и корректирует  ссылки
147 Serginio1
 
17.01.21
11:54
(141) Суть в том, что если структура в объекте, то она не на стеке! Смотри 146
148 Кирпич
 
17.01.21
11:55
(146) Ну как бы это понятно. Я просто показал пример, как создать в куче структуру и получить на неё ссылку.
149 Ненавижу 1С
 
гуру
17.01.21
11:58
(147) вопрос. Какая гарантия что объект не уничтожится, если есть ref ссылка на его поле-структуру?
Надо углубиться в то как выглядит внутри ref.
150 Serginio1
 
17.01.21
12:12
(149)
https://overcoder.net/q/495043/почему-метод-структуры-c-не-может-вернуть-ссылку-на-поле-а-метод-не-являющийся

Из комментариев
Документация» ( github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/… ) гласит, что «поля структуры экземпляра безопасны для возврата до тех пор, пока получатель безопасен для возврата». Здесь приёмник (параметр foo ) безопасен для возврата (потому что in ), поэтому, кажется, работает как задумано. В то время как первый случай покрыт "this", небезопасно возвращаться из членов структуры ".
151 Serginio1
 
17.01.21
12:15
150 То есть через расширения мы можежем вернуть ссылку на член структуры

struct Foo
{
    internal int _x;

    public ref readonly int MemberGetX() => ref _x;
    //                                          ^^^
    // Error CS8170: Struct members cannot return 'this' or other instance members by reference
}
Это приводит к ошибке. Члены CS8170 Struct не могут возвращать "этот" или другие члены экземпляра по ссылке. Тем не менее, выполнение одной и той же функции с использованием метода расширения не вызывает ошибки:

static class FooExtensions
{
    public static ref readonly int ExtensionGetX( this in Foo foo )
    {
        return ref foo._x;
    }
}

var f = new Foo();
        Console.WriteLine( f.X );
        f.GetXRefExtension() = 123;
        Console.WriteLine( f.X );

        // You can also do it without using an extension method, but the caller is required to specify ref:
        FooExtensions.GetXRef( ref f ) = 999;
        Console.WriteLine( f.X );

        /* Output:
         * 0
         * 123
         * 999
         */

То есть виден контекст и ref работает только на вызывающем стеке
152 Serginio1
 
17.01.21
14:19
140 + обсуждение и примеры
Introducing C# Source Generators
http://rsdn.org/forum/dotnet/7719189.flat
153 PR
 
17.01.21
21:32
Природа очистилась настолько, что с первой страницы ушли почти все темы Гени
Требовать и эффективности, и гибкости от одной и той же программы — все равно, что искать очаровательную и скромную жену... по-видимому, нам следует остановиться на чем-то одном из двух. Фредерик Брукс-младший