Skip to content

Instantly share code, notes, and snippets.

Тезисы

??? Тип dynamic можно использовать для определения аргументов типов обобщенных классов (ссылочный тип), структур (значимый тип), интерфейсов, делегатов или методов. Когда вы это делаете, компилятор конвертирует тип dynamic в Object и применяет DynamicAttribute к различным частям метаданных, где это необходимо. Обратите внимание, что обобщенный код, который вы используете, уже скомпилирован в соответствии с типом Object, и динамическая отправка не осуществляется, поскольку компилятор не производит код полезной нагрузки в обобщенном коде ???

Равенство - поля первого объекта попарно равны полям второго Тождество - первый объекта указывает на то же место в куче, что и второй объект == - это статический оператор, который может быть переопределен для каждого cочетания объектов, он не всегда вызывает Equals

1)Как реализован Object.Equals?

  public virtual Equals(object obj)
  {
    if(this == obj)
      return true;
 return false

1) Почему если переопределяешь Equals нужно переопределить GetHashCode?

Потому что каждый объект в C# можно поместить в виде ключа хэш таблицу, которая устроена так(объясняется устройство Dictionary<Key,Value>) Вычисляется хэш код объекта-ключа посредством вызова метода GetHashCode, далее по остатку от деления на количество корзин в словаре, объект помещается в корзину, в процессе возникают коллизии, т.е в одной корзине может лежать несколько объектов и тогда для поиска выбранного объекта при вызове dic[Key]:

  1. будет вызван GetHashCode объекта Key
  2. полученный хэш код будет взят по модулю количества корзин
  3. Будет выполнен проход по корзине и поиск нужного ключа, и если Equals не переопределен и указывает на адрес в памяти(ссылка), то при структурном определении GetHashCode, объект вообще не найдется, так как у двух экземпляров классов с одинаковыми поляеми разные ссылки, GetHashCode вернут одинаковые значения, но вызовы Equals - вернут false

2) Почему нельзя сохраняться хеши объектов для дальней

Видимость типа

CLR поддерживает (Family and Internal) - видимость только наследниками в сборке(и внутренними типами класса), в C# такого нет, есть только protected internal - видимость в своей сборке, видимость для наследников в любой сборке и видимость для внутренних типов, видимость для внутренних типов Кстати в nested классах доступны все закрытые члены внешнего класса:

 public class Outer
 {
     private int Value;

     public class Nested
 {

Константы

Константами могут быть только примитивные типы, потому что они сохраняются в метаданные модуля, при компиляции, когда компилятор доходит до объявления константы в коже, он смотрит сборку в которой она определена, и извлекает ее значение из метаданных, таким образом в IL код подставляется значение константы Константы нельзя изменять, т.к они запоминаются один только один раз при компиляции.Константы не могут быть статическими,т.к и так принадлежат типу в котором определены(хотя по факту, они размещаются в метаданных модуля)

Поля

Поля - данные класса, они могут иметь разные модификаторы, static - принадлежит типу в котором определяется, Instance - принадлежит экземпляру, readonly - запись разрешается только из кода конструктора, volatile - запрещает компилятору оптимизировать код обращающийся к полю Для статических полей память выделяется в пределах типа, который создается при загрузке типа в домен приложения(при первом обращении JIT компилятора к методу, который обращается к типу), для экз

До вызова конструктора класса происходит выделение памяти для его полей, инициализация экземплярных полей значениями по умолчанию и служебных полей(указателя на объект тип и индекса блока синхронизации) Беспараметрический конструктор класса должен быть вызван всегда, в отличии от конструктора структуры, это нужно для того, чтобы указать явное выделение памяти под объект, потому что простое объявление переменной ссылочного типа(например MyClass @class), еще не означает, что экземпляр этого типа будет успешно создан, т.к возможно MyClass имеет приватный беспараметрический конструктор, поэтому для создания класса нужно явно вызвать его конструктор

Почему инлайн инициализация полей привносит оверхед

Потому что при инлайн инициализации происходит инлайн проинициализированных полей в конструкторах(т.е в каждом объявленном конструкторе, если инлайнится экземплярное поле)

Где инициализируется экземплярные инлайн поля

В конструкторах экземпляра (нестатическом конструкторе)

Где инициализируется статичес

Когда вызывается конструктор типа

Когда происходит первое обращение JIT компилятор к методу, который обращается к типу, CLR следит, чтобы вызов происходил только один раз Бывают ситуации, когда из статического конструктора типа идет обращение к другому типу, и CLR должна вызвать статический конструктор этого типа, лучше избегать таких ситуаций Также возможно обращение к конструктору типа из разных потоков одновременно, тогда на него накладывается блокировка, как только первый поток начал исполнение, остальные потоки откатываются и больше этот конструктор не вызывается, тем самым получаем, что конструктор типа реализует шаблон singleton, и в нем можно инициализировать поля, которые также должны соответсовать этому шаблону

Сколько раз вызывается конструктор типа

Один

Как в CLR представлен конструктор типа ( какие модификаторы доступа используются)

private,static - private явно указывает что он вызывается только CLR

Как вызвать статический конструктор класса вручную

Используя метод Runtime.H

Как компилятор вызывает перегруженные операторы

Когда компилятор встречает в коде операторы, которые определены над пользовательскими типами, он идет в таблицу методов этих типов и ищет там определение оператора, для каждого оператора генерируется свое имя(например, для оператора (+) - op_Addition, перегрузку этих методов поддерживает CLR, и в IL коде методы перегрузки генерируются с именами типа op_Addition

Какие ограничения накладываются на перегрузку операторов

Перегруженные операторы должны быть определены в типе с которым взаимодействуют, а именно хотя бы один параметр в переопределении операторного метода должен иметь тип в котором объявлен, например вот такой код не скомпилируется: , а вот такой скомпилируется

Как выглядят перегруженные операторы в генерируемом IL коде

![](https://habrastorage.org/webt/8b/cp/bl/8bcpblacbnzt_viuwt8dobictwo

Как компилятор находит метод расширения определенный для типа

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

Может ли вызов extension метода выдать NRE? Почему?

Нет, вызов extension метода в CLR, записывается инструкцией call, которая не генерирует NRE, если вызывается на null (первый аргумент - this == null), поэтому метод расширения может быть вызван на null

Как создается Action в приведенном ниже скрине кода?

При создании action принимает два параметра - первый это контекс (на чем он выз

Как компилируются в IL методы с опциональными параметрами

Когда компилятор видит, что вызывается метод, который принимает больше параметров, чем ему передается, то он идет в метаданные модуля и смотрит, являются ли оставшиеся параметры опциональными Далее Рихтер пишет, что у этих параметров также есть атрибут DefaultParameterValue, в котором хранится значение по умолчанию и компилятор берет это значение и подставляет в вызывающий код, на в dotpeek я такой атрибут не нашел, тем не менее компилятор может проверить является ли этот атрибут опциональным и, если является, взять его значение из метаданных, кстати в IL коде метода с опциональными параметрами, также записываются default значения В итоге в IL коде происходит вызов со всеми заполненными параметрами:

Как происходит выз