вторник, 12 февраля 2019 г.

Не очень занимательный C#

Я как-то не особо лезу со своими комментариями к другим людям, а уж тем более к постам на хабре. Но вчера вот листал RSS-ленту и увидел интригующее название поста – "Занимательный C#. Пять примеров для кофе-брейка".

Итить, думаю, дай-ка зайду, посмотрю, что да как.

И вот первая загадка – что выдаст следующий код:

using System;

public struct SDummy : IDisposable
{
    private bool _dispose;
    public void Dispose() => _dispose = true;

    public bool GetDispose() => _dispose;

    private static void Main(string[] args)
    {
        var d = new SDummy();
        using (d)
        {
            Console.WriteLine(d.GetDispose());
        }
        Console.WriteLine(d.GetDispose());
    }
}

 

Ну, думаю, ок. Странно начинать с изменяемых структур и особенностей блока using, ну, ничего.

Открыл объяснение, а в нем говорится, что причина странного поведения в упаковке, дескать. Компилятор зовет Dispose метод через каст: ((IDisposable)myStruct).Dispose(), ну а каст структуры к интерфейсу, как известно, приводит к упаковке.

Вот те на, подумалось мне. Мало того, что черти дают достаточно невменяемые и не практичные загадки, так еще и ответы у них неверные.

Я добавил комментарий, после чего началось небольшое обсуждение. Дескать, сам Эрик "уже давно в фейсбуке работает" Липперт писал, что упаковка в блоке using быть должна и компилятор нарушает спеку и все такое... (хотя сегодня это и не так).

Меня смутило две вещи: то, как авторы загадки пытались выкрутиться и найти оправдание своей ошибке. Ну, и главное, что они полезли в дебри, не выяснив, что же в этих дебрях происходит. Да не просто полезли, они этим дебрям еще и учат.

Что в этом плохого?

Структуры в C# имеют две особенности – они являются "значениями", и могут располагаться напрямую в памяти контейнера (в стеке, регистрах и напрямую в других объектах).

Первое говорит о том, что в рантайме структуры по умолчанию копируются и то, что компилятор старается обеспечить семантику значения (т.е. неизменяемость) путем встраивания туда-сюда создание защитных копий (чтобы вызов метода или свойства, например, на неизменяемом поле ни в коем разе значение этого поля не поменял).

Второе же (место жизни структуры) может привести к упаковке т.е. к созданию копии структуры в куче.

Два этих отличия очень важны и в голове они, по-хорошему, должны лежать на разных полочках. Ибо каждый из них достаточно сложен, может меняться и развиваться по мере развития языка, да и проявляются эти особенности по-разному.

Вот, например, семантика значения и защитные копии стали гораздо более распространенной бедой с выходом C# 7x с их модификаторами ‘in’ и возвратом по неизменяемой ссылке (readonly refs) (вот, например, много буков по этому поводу - The ‘in’-modifier and the readonly structs in C#).

А самая большая беда проявляется с изменяемыми структурами, когда скрытая копия «прячет» изменения состояния, поскольку произойти они могут на временной копии. Не столь серьезное последствие заключается в некоторой потери производительности за счет создания копии, что решается путем использования readonly структур.

Упаковка же происходит совсем в других местах, при кастах к объектам/интерфейсам и в более экзотических случаях, типа при вызове методов из System.Object или System.ValueType (когда, например, Equals/GetHashCode не переопределены). А проявляется она путем увеличения давления на сборку мусора, что может аукнуться за счет тормозов сборщика мусора.

Так это я все к чему: учить других – это хорошо. Это просто здорово! Но касаясь всяких закоулков языка и рантайма, хорошо бы понимать, что, да как на самом деле происходит под капотом, да и желательно разбираться, почему происходит все именно так, а не иначе.

9 комментариев:

  1. Это же хабр, там всегда так

    ОтветитьУдалить
    Ответы
    1. Это да. Но в этом раз чего-то Остапа понесло:)

      Удалить
  2. Благодаря Вашему блогу, Сергей, совсем не "пару десятков" специалистов знают про защитные копии, а по-больше. Поэтому жалко, что статей давно не было.

    ОтветитьУдалить
    Ответы
    1. Андрей, спасибо за добрые слова. В последнее время я, в основном, писал на английском (https://blogs.msdn.microsoft.com/seteplia/), хотя в послднее время и туда редко пишу.

      Удалить
  3. Во-первых, Сергей, спасибо за ваш труд/хобби/энергию!
    Во-вторых, что Вы думаете по поводу того что, я, обращая внимание на задачу, которая описана здесь, а также похожего плана задачи, чувствую далеко НЕ«занимательное» настроение перед грядущим ввинчиванием в понимание того что вообще происходит?
    Как вы, ваши коллеги относитесь к такого рода ребусам, если уместно так выразиться?
    У меня сразу появляются мысли о том ,что надо бы снова сходить к истокам и почитать, видать, что-то подзабыл, в повседневной рутине не часто вижу такой код (может оно и к лучшему). Это вопрос проф компетенции, как вы считаете? Извините за ряд вопросов.

    ОтветитьУдалить
    Ответы
    1. > Как вы, ваши коллеги относитесь к такого рода ребусам, если уместно так выразиться?

      Так же, как к любым другим brain teaser-ам: "О! Интересная штука, о которой я не знал!" с последующим обсуждением/обдумыванием практичности загадки.

      > У меня сразу появляются мысли о том ,что надо бы снова сходить к истокам и почитать, видать, что-то подзабыл, в повседневной рутине не часто вижу такой код (может оно и к лучшему). Это вопрос проф компетенции, как вы считаете?

      Я обычно отношусь к подобным вещам проще: я много не знаю, и ничего такого в этом для меня нет. Если я вижу пробелы в фундаментальных знаниях (и эта загадка к таким знаниям не относится), то я думаю, как их заполнить.

      Я не считаю пробелы в подобных знаниях проблемами в проф. компетенции. Есть масса очень талантливых и успешных программистов, которые не ответять на подобные вопросы. Но это не делает их плохими специалистами. Иногда, даже наоборот.

      Удалить
  4. Вохновения и идей вам, Сергей! Ждем новых постов!

    ОтветитьУдалить
  5. Я видел комментарии людей, которые уже получили ссуду от г-на Бенджамина Ли, и я решил подать заявку в соответствии с их рекомендациями, и всего через 5 дней я подтвердил свою ссуду на моем банковском счете на общую сумму 850 000,00 долларов США, которую я запросил. Это действительно отличная новость, и я советую всем, кому нужен настоящий кредитор, подать заявку по электронной почте: 247officedept@gmail.com или WhatsApp: + 1-989-394-3740. Я счастлив, что получил ссуду, о которой просил.

    ОтветитьУдалить
  6. Отличные статьи! Сергей, вы хороший учитель. Таких мало на планете. Берегите себя

    ОтветитьУдалить