Неочевидное поведение блока using в C#

106
06 мая 2021, 19:40

Наткнулся на такой необычный код на METANIT. Объявлена структура:

public struct S : IDisposable
{
    private bool dispose;
    public void Dispose()
    {
        dispose = true;
    }
    public bool GetDispose()
    {
        return dispose;
    }
}

И есть такой код, который использует ее:

var s = new S();
using (s)
{
    Console.WriteLine(s.GetDispose());
}
Console.WriteLine(s.GetDispose());

Код выводит false, false, хотя казалось, что должно выводиться false, true. Самое интересное то, что если структуру S сделать классом, то будет выводиться ожидаемое значение :)

Объясните, что происходит за кулисами с переменной s? Почему для структуры будет false, false, а для класса - false, true

Answer 1

Ваш код с using разворачивается следующим образом:

var s = new S();
var using_s = s;
try {
    Console.WriteLine(s.GetDispose());
} finally {
    using_s.Dispose();
}
Console.WriteLine(s.GetDispose());

using вызыват Dispose не на вашей переменной s а на своей собственной переменной. И соответственно поле dispose обновляется не у переменной s.

Для примера можно рассмотреть следующий код:

using System;
public static class Program {
    public static void Main() {
        var s = new S();
        using (s)
        {
            s.Mark = 42;
            Console.WriteLine(s.GetDispose());
        }
        Console.WriteLine(s.GetDispose());
        var c1 = new C();
        using (c1)
        {
            c1.Mark = 42;
            Console.WriteLine(c1.GetDispose());
        }
        Console.WriteLine(c1.GetDispose());
        var c2 = new C();
        using (c2)
        {
            c2 = new C { Mark = 42 };
            Console.WriteLine(c2.GetDispose());
        }
        Console.WriteLine(c2.GetDispose());
    }
}
public struct S : IDisposable
{
    public int Mark;
    private bool dispose;
    public void Dispose()
    {
        Console.WriteLine(Mark);
        dispose = true;
    }
    public bool GetDispose()
    {
        return dispose;
    }
}
public class C : IDisposable
{
    public int Mark;
    private bool dispose;
    public void Dispose()
    {
        Console.WriteLine(Mark);
        dispose = true;
    }
    public bool GetDispose()
    {
        return dispose;
    }
}

В случаях s.Mark = 42; и c2 = new C { Mark = 42 }; назначение переменной внутри блока using не влияет на значение поля Mark, выводимое методом Dispose, так как using вызывает Dispose на своей собственной переменной. И компилятор даже выводит соответствующие предупреждение.

В случае с c1.Mark = 42; происходит не изменение переменной, а изменение объекта, на который она указывает.

READ ALSO
как из View передавать данные в контроллер

как из View передавать данные в контроллер

Я хочу записать checkbox'ы в разные строки таблицы, но не понимаю как из View передать несколько checkbox'ов как разные записиПри этом без ActionResult где...

100
Текстовое поле только для чисел [дубликат]

Текстовое поле только для чисел [дубликат]

Перепробовал уже очень много способов, несколько часов гуглежарезультат ноль

107
Как лучше читать читать данные из DBF в C#

Как лучше читать читать данные из DBF в C#

Задача: пользователи раз в месяц грузят данные от поставщиков в базуФайлы приходят разных размеров

110
Цикл for ведет себя странно (С# WinForms)

Цикл for ведет себя странно (С# WinForms)

В одном цикле я постоянно получаю SystemArgumentOutOfRangeException:

110