Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.56/9: Рейтинг темы: голосов - 9, средняя оценка - 4.56
3 / 2 / 1
Регистрация: 04.02.2023
Сообщений: 34
.NET 8

Переназначение ссылки с помощью оператора ref не работает

24.07.2024, 19:21. Показов 2364. Ответов 29
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Приветствую всех. Очень хотелось бы разобраться с данным вопросом, буду рад ответам.
https://learn.microsoft.com/en... t-operator
В данной статье говорится об операторе "ref"(не который используется в параметрах методах, а который служит некоторым псведонимом для ссылки на объект)
Например:
C#
1
2
3
4
5
6
7
8
9
10
class Program
{
    private static void Main(string[] args)
    {
       int i = 10;
       ref var alias = ref i;
       alias = 2000;
       Console.Write(i); //2000
    }
}
Вопрос в следующем. Судя по следующему коду, ссылка pr в методе Main() должна была стать null, так как мы переназначили ссылку в методе на статическое поле с null. Но программа ведет себя совершенно иначе. В чем же дело?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Program
{
    public static Program program;
 
    public void Bar(ref Program pr)
    {
        pr = ref program;
    }
 
    private static void Main(string[] args)
    {
        Program? pr = new();
        pr.Bar(ref pr);
        var b = pr is null;
        Console.WriteLine(b); //false
    }
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
24.07.2024, 19:21
Ответы с готовыми решениями:

Ссылки в C#: ref и out
всем привет, вопрос глупый состоит в том что код не компилируется .... public void Show() { Console.WriteLine("a: {0}, b:...

Передача ссылки по ссылке (ref)
Вопрос тут такого себе индивидуального характера. Встретился с этим впервые. Подскажите, почему метод Named, работает только тогда...

Программирование с помощью условного оператора и оператора выбора
Нужно решить две задачи. 1. Даны три числа. Определить является ли сумма каких-либо из них положительной. Указание к выполнению:...

29
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,228
24.07.2024, 19:44
А если так:
C#
1
2
3
4
public void Bar(ref Program pr)
    {
        pr = program;
    }
0
3 / 2 / 1
Регистрация: 04.02.2023
Сообщений: 34
24.07.2024, 19:51  [ТС]
Так, конечно, работает. Но это совершенно другое поведение.
В вашем случае я просто заберу объект с program, то есть присвою null. А в моем случае хотелось бы чтобы эти две ссылки стали связными.
То есть в моем дальнейшем поведении при написании program = new Program() у меня сразу же изменился бы и указатель pr в Main() как в первом примере. А в вашем случае изменится только статика, а ссылка в мейне так и будет указывать в пустоту
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,228
24.07.2024, 20:09
Avoid memory copying:
A typical assignment copies the value of the right hand side to the variable on the left hand side of the assignment.
A ref assignment copies the memory location of the variable on the right hand side to the variable on the left hand side. The ref now refers to the original variable
Ятп тут явно обозначается фундаментальная разница между ссылочнами и значимыми типами.
В переменной/выражении значимого типа хранится само значение - это т.н. self-contained экземпляры - именно поэтому мы можем делать финт:
C#
1
ref int v1 = ref v2;
А в переменной/выражении ссылочного типа хранится ссылка на объект в куче, но при ref assignment копируется при этом адрес уже самой этой ссылки.
А вот ваш случай:
C#
1
2
3
4
public void Bar(ref Program pr)
    {
        pr = ref program; //copies the memory location - копирует адрес, который не null
    }
Вы берете адрес переменной (который, очевидно, не null).

Цитата Сообщение от EX_MOON Посмотреть сообщение
А в моем случае хотелось бы чтобы эти две ссылки стали связными.
А со ссылочными типами так и не надо работать. Чтобы две ссылки стали связными, надо делать так:
C#
1
2
3
var obj = new object();
var obj2 = obj;
ReferenceEquals(obj, obj2); //true
Добавлено через 4 минуты
Цитата Сообщение от IamRain Посмотреть сообщение
А вот ваш случай:
В самой переменной-ссылке (читай ссылке, адрес уже НЕ null, но ссылается она на null, это да).

Добавлено через 2 минуты
Ятп ref assignment придумали, чтобы избавиться от копирования больших кусков памяти при передаче/возврате у значимых типов, то есть сохранить семантику передачи по ссылке.

А с непосредственно ссылочными типами этого делать не надо, то есть вы полезли не туда куда надо.

Добавлено через 2 минуты
Покажите мне хотя бы один пример из кодовой базы Microsoft где ref assignment оперируют со ссылочными типами.
0
3 / 2 / 1
Регистрация: 04.02.2023
Сообщений: 34
24.07.2024, 20:13  [ТС]
Не совсем понял эту "очевидность". Я понимаю стандартное присваивание референс типов, но речь же идет о другом
Приведу ещё раз пример.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//Присваивание ссылок
        //case1 - принимает разные значения с каждым запуском программы
        var program = new Program();
        ref var program2 = ref program;
        if (case1)
        {
            program = null; // program2 тоже становится null
        }
        else
        {
            program2 = null; //program тоже становится null;
        }
        //при обычном присваивании было бы так:
        var program = new Program();
        var program2 = program;
        if (case1)
        {
            program = null; //program2 все ещё ссылается на объект, на который ссылалась program
        }
 
        else
        {
            program2 = null; //program все ещё ссылается на свой объект
        }
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,228
24.07.2024, 20:14
То есть сам факт, что вы в принципе ссылочный тип передаете по ref, уже говорит о том, что скорее всего (не всегда) вы идете не туда.
0
3 / 2 / 1
Регистрация: 04.02.2023
Сообщений: 34
24.07.2024, 20:17  [ТС]
Мы же сейчас не говорим о том как правильно/неправильно. Хотелось бы понять структуру самого языка и его возможностей.
И насчет кодовой базы:
В классе словаря есть подобный код.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
  
 public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
        {
            ref TValue valRef = ref FindValue(key);
            if (!Unsafe.IsNullRef(ref valRef))
            {
                value = valRef;
                return true;
            }
 
            value = default;
            return false;
        }
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,228
24.07.2024, 20:44
Чтобы супер точно ответить на ваш вопрос, тут уже надо в IL-код смотреть.
C#
1
2
3
var program = new Program();
ref var program2 = ref program;
program = null;
Превращается в такое:
Code
1
2
3
4
5
6
7
8
9
10
11
12
IL_0000: newobj       instance void Program::.ctor()  // создать объект
    IL_0005: stloc.0      // вернуть ссылку на него в первую переменную - в список локальных переменных под индексом 0
 
    // [2 1 - 2 32]
    IL_0006: ldloca.s     program // положить адрес локальной переменной - ятп program - в evaluation stack
    IL_0008: stloc.1      // взять значение - адрес - из evaluation stack и  положить в переменную под индексом 1, program2
                                 // то есть program2 хранит адрес локальной переменной program - что и указывал в цитате
 
    // [3 1 - 3 16]
    IL_0009: ldnull //положить null в evaluation stack
    IL_000a: stloc.0      // переложить обратно этот null в переменную program
    IL_000b: ret
Получается у нас нечто указателя на указатель, когда первый начинает указывать на null, то второй после двойного разыменования тоже будет указывать на null.
0
3085 / 3240 / 556
Регистрация: 17.02.2019
Сообщений: 5,154
24.07.2024, 20:47
https://metanit.com/sharp/tutorial/2.27.php
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,228
24.07.2024, 21:23
Второй случай:
C#
1
2
3
4
public void Bar(ref Program pr)
    {
        pr = ref program;
    }
C#
1
2
3
4
5
6
  // [7 9 - 7 26]
    IL_0001: ldsflda      class Program Program::program //pushes the [B]address[/B] of the static field onto the evaluation stack
    IL_0006: starg.s      pr // копирует адрес из evaluation stack в переменную pr - она хранит АДРЕС!
 
    // [8 5 - 8 6]
    IL_0008: ret
Теперь такой вариант:
C#
1
2
3
4
public void Bar(ref Program pr)
    {
        pr = program;
    }
Code
1
2
3
4
5
6
7
8
9
10
 .method public hidebysig instance void
    Bar(
      class Program& pr
    ) cil managed
 
 
// [7 9 - 7 22]
    IL_0001: ldarg.1      //  загружает переменную c индексом 1 (0 у экземпляра всегда) в eval.stack - то есть АДРЕС
    IL_0002: ldsfld       class Program Program::program - pushes the VALUE of the static field onto evaluation stack
    IL_0007: stind.ref //stores an object reference value at a supplied ADDRESS (of type native int, & or *)
Добавлено через 4 минуты
То есть разница в командах, как уже сказал:

Ref assignment в методе Bar:
Code
1
2
IL_0001: ldsflda      class Program Program::program //pushes the ADDRESS of the static field onto the evaluation stack
IL_0006: starg.s      pr // копирует адрес из evaluation stack в переменную pr - она хранит АДРЕС!

Обычное value assignment на ссылочных типах в методе Bar:
Code
1
2
3
IL_0001: ldarg.1      //  загружает переменную c индексом 1 (0 у экземпляра всегда) в eval.stack - то есть АДРЕС
IL_0002: ldsfld       class Program Program::program - pushes the VALUE of the static field onto evaluation stack
IL_0007: stind.ref //stores an object reference VALUE at a supplied ADDRESS (of type native int, & or *)
В целом то что я интуитивно и отвечал - сохраняет адрес и сохраняет значение.
Разные команды: starg.s VS stind.ref.

Цитата Сообщение от EX_MOON Посмотреть сообщение
if (!Unsafe.IsNullRef(ref valRef))
Ну видите, не все так просто, свое cпециальное API используется.

Добавлено через 18 минут
Условно, две команды:
1. Обычное reference assignment, но слева АДРЕС (мб тоже самое что и просто имя переменной).
stind.ref address value - по указанному адресу записать значение.

2. Необычное ref assignment над reference type variables (в конкретном частном случае):
Тут пара команд для наглядности:
Code
1
2
3
IL_0001: ldsflda      class Program Program::program // положить АДРЕС статической переменной в eval.stack
IL_0006: starg.s      pr // store the value (которое АДРЕС) on top of the eval.stack in the argument slot at a specified index, 
                                  // short form.
Добавлено через 2 минуты
Я не знаю, что тут еще добавить. В такие вещи не нужно погружаться ятп, до тех пор, пока вы не пилите сам Roslyn.
0
3 / 2 / 1
Регистрация: 04.02.2023
Сообщений: 34
24.07.2024, 21:31  [ТС]
Не ожидал, что все дойдет до IL кода. Говоря, честно, я все равно до конца не понял почему в методе мейн ссылка никак не меняется, хотя, кстати, если попробовать в том же методе бар после присвоения аргументу Program указателя на статическое поле изменить этот указатель, то статическое поле меняется, но опять таки эта связь сохраняется только в самом методе, а со стороны вызова остается все точно так же. Согласен с последним, это просто излишнее любопытство, которые обычно до хорошего не доводит и только разрушает уже устоявшиеся слои абстракций.
Спасибо за ответы
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
25.07.2024, 11:06
Цитата Сообщение от EX_MOON Посмотреть сообщение
я все равно до конца не понял почему в методе мейн ссылка никак не меняется
Надо сначала немного прояснить терминологию.
1. Переменная — это именованный участок памяти, доступный для чтения/записи через имя переменной.
2. Ref-переменная — это алиас/псевдоним/дополнительное имя для некоего участкая памяти.
3. Оператор присваивания = — это операция записи значения по правую сторону в память, доступную через имя по левую сторону.
4. Оператор ref-присваивания = ref — это привязка дополнительного имени по левую сторону к памяти, доступную через имя по правую сторону.

При вызове метода Bar алиас Bar.pr привязывается к участку памяти, доступному через имя Main.pr. Все дальнейшие манипуляции с Bar.pr будут производиться над тем же участком памяти, который доступен через Main.pr
При выполнении операции pr = ref program; на 7-й строчке псевдоним Bar.pr отвязывается от участка памяти Main.pr и привязывается к участку памяти, доступному через Program.program. Все дальнейшие манипуляции с Bar.pr будут производиться над тем же участком памяти, который доступен через Program.program.
Ни на память, доступную через Main.pr, ни на хранящееся в этой памяти значение это никак не влияет.

Примерное представление памяти на момент входа в Bar:
Адрес памятиЗначениеДоступно через
0xAnullProgram.program
0xBnew Program()Main.pr, Bar.pr

Оно же, но после выполнения 7-й строчки:
Адрес памятиЗначениеДоступно через
0xAnullProgram.program, Bar.pr
0xBnew Program()Main.pr

Моральный эквивалент этого же примера, но без дополнительных функций:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
{
    public static Program program;
    
    private static void Main(string[] args)
    {
        Program? mainPr = new();
        
        // Привязка псевдонима barPr к памяти, доступной через mainPr
        ref Program? barPr = ref mainPr;
        
        // Отвязка псевдонима barPr от памяти, доступной через mainPr и привязка к памяти, доступной через program
        barPr = ref program;
        
        Console.WriteLine(mainPr is null); //false
        Console.WriteLine(barPr is null); // true
    }
}
Если хочется аналогии, то неплохо подходит работа с файлами в Windows.
Имеется два файла: C:\mainPr.txt и C:\program.txt
Создается ярлык на C:\mainPr.txt с именем barPr.lnk. Если открыть блокнот через этот ярлык, что-то напечатать и сохранить, то это будет аналогично операции присваивания: barPr = ....
Если зайти в свойства ярлыка и изменить поле Target на C:\program.txt, то это эквивалентно операции barPr = ref program.
Ни на содержимое C:\mainPr.txt, ни на то, какой файл откроется щелчком по mainPr.txt, изменение свойств ярлыка никак не влияет.
3
151 / 135 / 29
Регистрация: 02.07.2013
Сообщений: 962
25.07.2024, 12:19
Цитата Сообщение от kolorotur Посмотреть сообщение
При вызове метода Bar алиас Bar.pr привязывается к участку памяти, доступному через имя Main.pr. Все дальнейшие манипуляции с Bar.pr будут производиться над тем же участком памяти, который доступен через Main.pr

то есть окажется, что "поле" объекта указывает на память не лежащую в объекте? или тут у точки Main.pr Bar.pr другой смысл?

это работает только с локальными переменными?
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
25.07.2024, 13:12
Цитата Сообщение от golosalex Посмотреть сообщение
то есть окажется, что "поле" объекта указывает на память не лежащую в объекте?
Поле объекта в этом примере только одно: program.
Память под эту переменную — часть экземпляра.

Цитата Сообщение от golosalex Посмотреть сообщение
или тут у точки Main.pr Bar.pr другой смысл?
Это локальные переменные методов — не поля.

Цитата Сообщение от golosalex Посмотреть сообщение
это работает только с локальными переменными?
В структурах можно объявлять ref-поля, но там строгие правила к чему их можно привязывать — чтобы не привязаться к локальной ниже по стеку, например.
0
3 / 2 / 1
Регистрация: 04.02.2023
Сообщений: 34
25.07.2024, 13:45  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
Надо сначала немного прояснить терминологию.
1. Переменная — это именованный участок памяти, доступный для чтения/записи через имя переменной.
2. Ref-переменная — это алиас/псевдоним/дополнительное имя для некоего участкая памяти.
3. Оператор присваивания = — это операция записи значения по правую сторону в память, доступную через имя по левую сторону.
4. Оператор ref-присваивания = ref — это привязка дополнительного имени по левую сторону к памяти, доступную через имя по правую сторону.

При вызове метода Bar алиас Bar.pr привязывается к участку памяти, доступному через имя Main.pr. Все дальнейшие манипуляции с Bar.pr будут производиться над тем же участком памяти, который доступен через Main.pr
При выполнении операции pr = ref program; на 7-й строчке псевдоним Bar.pr отвязывается от участка памяти Main.pr и привязывается к участку памяти, доступному через Program.program. Все дальнейшие манипуляции с Bar.pr будут производиться над тем же участком памяти, который доступен через Program.program.
Ни на память, доступную через Main.pr, ни на хранящееся в этой памяти значение это никак не влияет.

Примерное представление памяти на момент входа в Bar:
Адрес памятиЗначениеДоступно через
0xAnullProgram.program
0xBnew Program()Main.pr, Bar.pr

Оно же, но после выполнения 7-й строчки:
Адрес памятиЗначениеДоступно через
0xAnullProgram.program, Bar.pr
0xBnew Program()Main.pr

Моральный эквивалент этого же примера, но без дополнительных функций:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
{
    public static Program program;
    
    private static void Main(string[] args)
    {
        Program? mainPr = new();
        
        // Привязка псевдонима barPr к памяти, доступной через mainPr
        ref Program? barPr = ref mainPr;
        
        // Отвязка псевдонима barPr от памяти, доступной через mainPr и привязка к памяти, доступной через program
        barPr = ref program;
        
        Console.WriteLine(mainPr is null); //false
        Console.WriteLine(barPr is null); // true
    }
}
Если хочется аналогии, то неплохо подходит работа с файлами в Windows.
Имеется два файла: C:\mainPr.txt и C:\program.txt
Создается ярлык на C:\mainPr.txt с именем barPr.lnk. Если открыть блокнот через этот ярлык, что-то напечатать и сохранить, то это будет аналогично операции присваивания: barPr = ....
Если зайти в свойства ярлыка и изменить поле Target на C:\program.txt, то это эквивалентно операции barPr = ref program.
Ни на содержимое C:\mainPr.txt, ни на то, какой файл откроется щелчком по mainPr.txt, изменение свойств ярлыка никак не влияет.
Спасибо. Теперь понял. Только вопрос теперь в другом
В документации майков написано следующее:

https://learn.microsoft.com/ru... parameters

При передаче типа значения по значению:
Если метод назначает параметр для ссылки на другой объект, эти изменения не отображаются из вызывающего объекта.
Если метод изменяет состояние объекта, на который ссылается параметр, эти изменения не отображаются вызывающим объектом.
При передаче ссылочного типа по значению:
Если метод назначает параметр для ссылки на другой объект, эти изменения не отображаются из вызывающего объекта.
Если метод изменяет состояние объекта, на который ссылается параметр, эти изменения отображаются из вызывающего объекта.
При передаче типа значения по ссылке:
Если метод назначает параметр для ссылки на другой объект, эти изменения не отображаются из вызывающего объекта.
Если метод изменяет состояние объекта, на который ссылается параметр, эти изменения отображаются из вызывающего объекта.
При передаче ссылочного типа по ссылке:
Если метод назначает параметр для ссылки на другой объект, эти изменения видны вызывающему объекту.
Если метод изменяет состояние объекта, на который ссылается параметр, эти изменения отображаются из вызывающего объекта.
Казалось бы, что значит
Если метод назначает параметр для ссылки на другой объект, эти изменения видны вызывающему объекту
По-началу мне показалось, что это значит банальную смену ссылки, только обычное переприсвоение в методе.
То есть что-то подобное:
C#
1
2
3
4
5
6
7
8
9
10
11
class Program
{
public static void Foo(ref TYPE t)
{t = null или t = new TYPE();
}
 
public static void Main()
{
TYPE a = new();
Foo(ref a)
}
Однако, в соответствии с документацией про тип значения по ссылке:
Если метод назначает параметр для ссылки на другой объект, эти изменения не отображаются из вызывающего объекта
Получается речь совсем не об этом.
Я решил поискать дальше и нашел следующее:

https://github.com/dotnet/docs/issues/32853
Оказывается речь шла про те самые ref assignment, из чего можно сделать вывод, что в пункте про ссылочные типы по ссылке говорится о том же самом:
Если метод назначает параметр для ссылки на другой объект, эти изменения видны вызывающему объекту.
Но тогда получается противоречие. Мы же только что доказали, что такого быть не может. Ошибка в документации или я чего-то недопонимаю?
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
25.07.2024, 14:36
Цитата Сообщение от EX_MOON Посмотреть сообщение
Ошибка в документации или я чего-то недопонимаю?
Похоже, в документации просто плохо подобраны слова.
Одна и та же фраза "assigns the parameter to refer to a different object" может означать разные операции в зависимости от типа переменной.
Третий пункт говорит о переменных значимого типа. Они не могут ни на что ссылаться, потому здесь эта фраза скорее всего подразумевает оператор = ref.
Четвертый пункт говорит о переменных ссылочного типа, которые могут ссылаться на объекты, потому там скорее всего идет речь про обычный оператор присваивания =.
0
3 / 2 / 1
Регистрация: 04.02.2023
Сообщений: 34
25.07.2024, 17:19  [ТС]
Видимо, и вправду так. Думаю тему можно закрывать.
Всем спасибо за ответы
0
 Аватар для INexteR
17 / 16 / 1
Регистрация: 25.01.2023
Сообщений: 468
22.08.2024, 12:18
IamRain, вопрос касаемо ref readonly:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
static void Main()
{
    object spinLock = new SpinLock();
    static void CallGo(object state) => Go(ref Unsafe.Unbox<SpinLock>(state!));
    var tasks = new Task[3];
    for (int i = -1; ++i < tasks.Length;)
    {
        tasks[i] = Task.Factory.StartNew(CallGo!, spinLock);
    }
    try
    {
        Task.WaitAll(tasks);
    }
    catch (AggregateException ae)
    {
        foreach (var ex in ae.InnerExceptions)
            Console.WriteLine(ex.Message);
    }
    Console.WriteLine(n is 30_000);
}
 
static int n;
 
static void Go(ref SpinLock spinLock)
{
    ref readonly SpinLock sl = ref spinLock;
    for (int i = -1; ++i < 10_000;)
    {
        bool lockTaken = false;
        try
        {
            sl.Enter(ref lockTaken);
            n++;
        }
        finally
        {
            if (lockTaken) sl.Exit();
        }
    }
}
Если убрать строку
C#
1
ref readonly SpinLock sl = ref spinLock;
И использовать оригинальный аргумент, то код работает штатно. Но вместе с ней каждый поток пула выбрасывает SynchronizationLockException. Какой эффект привносит данная строка?
1
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,228
22.08.2024, 13:05
Цитата Сообщение от INexteR Посмотреть сообщение
то код работает штатно
Что именно предполгается под штатной работой? Мой вариант выдает три исключения - на каждой таске:
Bash
1
2
3
4
5
/home/ryan/Projects/Rider/ConsoleApp3.SpinLock/ConsoleApp3.SpinLock/bin/Debug/net8.0/ConsoleApp3.SpinLock
The calling thread does not hold the lock.
The calling thread does not hold the lock.
The calling thread does not hold the lock.
False
Добавлено через 5 минут
SpinLock - жирная структура, поэтому для быстродействия имеет смысл передавать ее по Managed Pointer-у (по ссылке), это да.
Однако следующая строка создает ref readonly pointer, который запрещает изменять состояние экземпляра, через создание defensive copy - метод вызывается именно на defensive copy, а не на оригинале.
+ дополнительно этой строкой вы очевидно производите (при будущих вызовах методов над этой переменной) копирование, чего так пытались избежать, передавая по ref-у.
C#
1
ref readonly SpinLock sl = ref spinLock;
Короче, это кусок строки - мусорный и даже вредный.

Добавлено через 1 минуту
Цитата Сообщение от IamRain Посмотреть сообщение
Короче, это кусок строки - мусорный и даже вредный.
После удаления - вывод просто True.

Добавлено через 2 минуты
C#
1
The calling thread does not hold the lock.
Скорее всего это возникает на строке:
C#
1
2
3
4
finally
            {
                if (lockTaken) sl.Exit();
            }
Потому что sl.Enter(..) не изменил стейт иммутабельной ссылки.

Добавлено через 17 минут
INexteR, а не подскажете, чем могут оказаться полезными кастомные ref-like структуры?
Просто интересно.
1
 Аватар для INexteR
17 / 16 / 1
Регистрация: 25.01.2023
Сообщений: 468
22.08.2024, 13:26
Цитата Сообщение от IamRain Посмотреть сообщение
Что именно предполгается под штатной работой?
Предполагается исключение первой строки метода Go из исходного кода с последующим выводом лишь True.
Цитата Сообщение от IamRain Посмотреть сообщение
После удаления - вывод просто True.
Да, предполагается именно это.
Цитата Сообщение от IamRain Посмотреть сообщение
имеет смысл передавать ее по Managed Pointer-у
А иначе никак, ведь она скопируется и каждый поток получит свой экземпляр, в результате чего синхронизировать доступ к секции не получится.
Цитата Сообщение от IamRain Посмотреть сообщение
который запрещает изменять состояние экземпляра, через создание defensive copy
А, ясно, я так и думал, что каким-то образом создается копия.
Цитата Сообщение от IamRain Посмотреть сообщение
Короче, это кусок строки - мусорный и даже вредный.
Да, справедливо, эта строка добавлена только из академического интереса

Добавлено через 16 минут
Цитата Сообщение от IamRain Посмотреть сообщение
кастомные ref-like структуры?
Если уж и писать такую, то скорее всего с целью служить определенным видам микро-оптимизации, особенно полезных в горячих точках, критичных к производительности. В частности, для сведения выделения управляемой памяти к минимуму (сокращая нагрузку на сборщик мусора)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
22.08.2024, 13:26
Помогаю со студенческими работами здесь

Как сделать данный код с помощью OUT\REF ???
нужно этот же код, сделать через OUT и REF (методы процедуры) как это сделать ? using System; using System.Collections.Generic; ...

Переназначение линейного входа на микрофонный не работает
Здравствуйте! Розовый задний вход был бесповоротно сломан. Знаю, что можно использовать синий (линейный) вход для замены. Подключила штекер...

Ref string не работает
Совершенно не понял, по каким причинам string даже через ref не хочет меняться (уже странно было то, что ссылочный тип и его значения по...

Не работает ref параметр
Пишет,что не удалось скопировать. Вот код: /* * Created by SharpDevelop. * User: User * Date: 05.02.2016 * Time: 21:40 ...

Как работает ref?
Изучая описание ключевого слова ref я не совсем понял его смысла, я понял что ref равнозначно return принимаемой переменной, то есть ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
Кто-нибудь знает, где можно бесплатно получить настольный компьютер или ноутбук? США.
Programma_Boinc 26.12.2025
Нашел на реддите интересную статью под названием Anyone know where to get a free Desktop or Laptop? Ниже её машинный перевод. После долгих разбирательств я наконец-то вернула себе. . .
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Рецензия / Мнение/ Перевод Нашел на реддите интересную статью под названием The Thinkpad X220 Tablet is the best budget school laptop period . Ниже её машинный перевод. Thinkpad X220 Tablet —. . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru