02.01. C# language basic

Поделиться
HTML-код
  • Опубликовано: 18 сен 2024

Комментарии • 4

  • @KybaLioN66
    @KybaLioN66 6 дней назад

    Спасибо за видео.
    Хочу прояснить один момент. В C++ есть встроенные типы (int, float, double и т. д.).
    Это всё так сказать честные типы, без каких либо обёрток. В Java тоже есть такие примитивные типы, но на них когда пытаёшься вызвать метод то компилятор содаёт reference wrapper. для примера для int будет создан эксземпляр класса Integer. Как я понимаю встроенные коллекции даже не могут хранить value types.
    В С# не много путаница. У нас все value types это струсктуры которые наследованы от структуры ValueType. Как я понимаю System.Int32 инкапсулирует примитивный тип int как в С++. Но в этом классе есть m_value который тоже экземпляр класса System.Int32. Вопрос как runtime создает примитивный тип в нутри себя ?
    Вопрос может быть не точен. просото для меня runtime это программа написанное на С++, который выполняет IL код. И если где то определено value type, для него он должен создать примитивный тип.

    • @mihail_romanov
      @mihail_romanov  6 дней назад

      Вопрос хороший прямо досконально не расскажу - я и не на столько глубоко погружен в платформу и компилятор, и в комментарии это сделать сложно...
      Но если в 2-х словах, то:
      1. На уровне runtime (IL) есть поддержка примитивных типов данных (int32, int64, ...). Она включает в себя: операции с разными типами (загрузка в стек, выгрузка, арифметические операции, ...), объявление локальных переменных в процедурах (потом можно к ним обращаться в командах), использование примитивных типов в качестве полей в объектах и структурах (там тоже специальные команды типа "загрузи поле XXX объявленное в типе YYY, из объекта ZZZ"), при передаче параметров в методы, и возврате результатов
      Т.е. на уровне IL int32 это именно простейший тип, не структура и не класс.
      2. В BCL такие типы объявлены как структуры с одним единственным полем m_value с тем же типом, как и сама структура. Т.е., с точки зрения C# (да и вообще .Net) - недопустимая вещь!
      Поэтому структура System.Int32 (как и остальные примитивные типы) при компиляции обрабатывается специальным образом:
      - игнорируется, то структура используется внутри себя (я про поле m_value)
      - т.к. у структуры, в отличие от объекта, нет всяких скрытых дополнительных полей (ссылки на метаданные, поля sync, ...) то System.Int32 в памяти хранится как просто число. Поэтому ссылаемся мы на , или на , или на поле в - генерируется один и тот же код - который работает с IL-типом
      Иными словами, если бы это была бы обычная структура, то при компиляции внутренних методов, которые обращаются к полю использовались бы конструкции типа или а вот тут ничего похожего не будет.
      Ну и в обычном коде, везде, где идет работа с System.Int32 при генерации IL просто происходит замена на int32.
      Кстати, у описания типа (класса Type) есть поле , которое и позволяет узнать - является ли тип примитивным или это обычная структура
      3. Из того, что осталось - это вызов методов. Здесь примитивные типы ничем не отличаются от обычных valuetype:
      - когда вызывается статический метод, то у него нет никаких неявных параметров, мы обязаны аргументы объявить явно. Ну и структура по умолчанию передается по значению (копированием).
      - если вызывается обычный (не виртуальный) экземплярный метод, то у него есть один неявный параметр - ссылка на экземпляр структуры, для которой он вызывается. Здесь всё как и у объектов - загружается адрес.
      Например
      Int32 a = 5;
      var t= a.CompareTo(44);
      Перед вызовом CompareTo, в стек будет загружен адрес локальной переменной и константа 44.
      - вызов перегруженных виртуальных методов (т.к. valuetype друг от друга наследовать нельзя, то виртуальные методы - только от Object: GetType(), Equal(), ToString()), вот для них прежде чем вызывать виртуальный метод, делается операция boxing - она создает в куче объект (со всеми доп. полями - той же ссылкой на метаданные!) и копирует в него содержимое структуры, а уже потом делает (и этим boxing опасен - создается копия, а не ссылка!)
      Но у valuetype есть определенная оптимизация. Для них вместо прямого box вызывается инструкция , которая сначала проверяет - реально ли нужен boxing (если вызывается перегруженный метод - тот же ToString(), то нет) и только если он необходим, она делает .
      P.S. Прошу прощения - получилось немного сумбурно. Я, к сожалению, не нашел нигде нормальной статьи, где бы это было описано, поэтому даже ссылку не могу дать, где бы почитать подробнее.
      Многие ссылаются вот на эту статью web.archive.org/web/20141124091806/weblogs.asp.net/dixin/understanding-net-primitive-types, но там тоже не всё и без особых подробностей.

    • @KybaLioN66
      @KybaLioN66 6 дней назад +1

      @@mihail_romanov Спасибо вам огромное. За ваш большой труд. Собрать всю эту информацию во едино не простая задача. Да, тема доволна сложная, есть слой так сказать магии которая не понятна. Так как сейчас только начинаю изучение, то можно и опустить такие моменты для начало. Но по этой теме может помочь давольна популярная книга CLR via С# by Jeffrey Richter.

    • @mihail_romanov
      @mihail_romanov  6 дней назад

      CLR via С# действительно очень неплохая книга (я вообще могу сказать, что все книги, что мне попадались у Рихтера, были феноменальными по охвату и глубине), но:
      - книга довольно сложная и новичка может даже запутать. Т.е. я бы её не стал читать как первую книгу по .Net/C# (да и язык там практически не упоминается)
      - книга старая. Если не ошибаюсь, последнее издание было для .Net Framework 4.5. С выходом Core и более поздних версий .Net что-то поменялось, что-то стало неактуальным и нерекомендуемым к использованию, для чего-то появились другие механизмы, ...
      К сожалению, Джеффри больше не пишет книг, а других авторов такого уровня я просто не знаю.