Как стать автором
Обновить

Комментарии 62

Любому двоичному коду .NET может быть сопоставлен внешний конфигурационный файл XML. Этот файл располагается в том же каталоге, и имеет такое же имя с добавленным в конце словом .CONFIG;

Что вы хотели этим сказать?
Я думаю, автор имел в виду app.config и подобные файлы конфигурации.
В контексте .net, перевести assembly как двоичный код — это сильно.
8 пункт — неправда. Запрос выполняется, если он возвращает скаляр(Count, Sum, First...). Даже если на нем вызвать foreach (то есть, выполнить GetEnumerator()) он будет выполнен так лениво, как только можно. То есть, такое выражение не выполнится сразу:
	var s =  from f in Foo()
          	group f by f%2;


Если быть точным, то выполнено оно будет лениво, однако для получения второй группы нужно будет проитерировать всю исходную последовательность.
Максимальная ленивость никак не поможет, если мы упорядочиваем бесконечную коллекцию.
Если мы не получаем никакого скалярного значения после упорядочивания и не производим итерацию, как в примере сверху — еще как поможет :)
Многое в статье — просто цитаты из Рихтера.
И про пункт 20 — можно поставить бряк на автосвойство. В 2015 студии это можно даже сделать мышкой.
Именно, рад что заметил это. Название намекает «Интересные заметки по C# и CLR». Сколько именно из Джеффри Рихтера не считал.
Как быстро в рантайме найти петли потоков (dead-locks)?
— !dlk

Не очень понятно, что вы имели в виду.
Видимо, это относится к отладке в Windbg с подгруженной библиотекой SOS (Son of Strike).
Мне статья понравилась, но есть замечание по терминологии в 1 пункте
Объекты содержат в себе статические поля и все методы. Экземпляры содержат только не статические поля. Это значит, что методы не дублируются в каждом экземпляре, и здесь применяется паттерн Flyweight.
Есть понятие («класс») и есть понятие («экземпляр класса», что по сути == «объект»). Используя такие термины, никогда не возникнет путаницы при изучении нового языка.

Также мне интересно, есть ли у разработчиков понимание, почему один и тот же метод у разных экземпляров класса в разных потоках не пересекается с другими и не меняет их значения, ведь по сути инструкции метода не дублируются?
Также мне интересно, есть ли у разработчиков понимание, почему один и тот же метод у разных экземпляров класса в разных потоках не пересекается с другими и не меняет их значения, ведь по сути инструкции метода не дублируются?

Я думаю не у всех =) Это уже относится к более фундаментальным знаниям (многопоточность в ОС, стек, регистры процессора, контекст потока и т.д.), а не напрямую к C#.
А в MSIL у методов экземляра есть неявный параметр this, что и позволяет работать с конкретным объектом.
Когда this == null: невыдуманная история из мира CLR
В целом статься получилась довольно сумбурной и содержит довольно много ошибок. Возможно автор злоупотреблял переводчиком или недостаточно понимает описываемые механизмы, но в любом случае статью нужно серьезно переработать.
Из ошибок которые сразу бросились в глаза это п.35 — VolatileWrite, VolatileRead — это не атомарные операции, по пункту Б — я даже не знаю как это написать, но в целом весь пункт сплошная ошибка. В англоязычной литературе взаимным запиранием называется мьютекс (Mutual Exclusion). Interlocked — класс предоставляет набор атомарных операций и не имеет ничего общего с мьютексом.
п.30 — написано не имеет отношения к ковариации и контравариации msdn.microsoft.com/ru-ru/library/dd799517(v=vs.110).aspx
Не использовал переводчик при прочтении книги написанной на Русском языке.
п. 30 дополнил примерами, он правильный.
По умолчанию обобщенные типы инвариантны. Еще обобщенные классы называют открытыми, в рантайме они закрываются конкретными типами «int», «string», например. И это разные типы, статические поля в них будут тоже разными.
Какое отношение выделенное предложение имеет к ковариантности, контрвариантности или инвариантности?
К перечисленным вами слов — никакого, именно поэтому использовано слово «еще», это как дополнение к «обобщенные типы».
все еще не правильный, возможность использования наследника вместо указанного типа не есть «преобразование в одну сторону». Преобразование типов возможно вообще без наследования например.
Возможно у вас проблемы с терминологией, но в нашем деле это очень важный аспект и нужно очень тщательно подбирать слова для описания процессов, особенно на русском.
За что купил за то и продал, это цитаты.
Да, есть явные и неявные числовые преобразования с потерей точности, но имелось ввиду другое.
Местами бред полный.

У структурных типов и примитивных (byte,int,long...) нет блока синхронизации

Исключения медленно работают, т.к. происходит переход в режим ядра.

в) !DEBUG == RELEASE (на всякий случай).

31) Методы расширения

А это реализация паттерна Visitor в .Net.
Местами бред полный.

У структурных типов и примитивных (byte,int,long...) нет блока синхронизации

Я конечно не специалист, но разве у неупакованных ссылочных типов есть индекс блока синхронизации? По-моему как раз у них его и нет.
Safe Thread Synchronization
Правда тут ссылка на того же Рихтера, из чьей книги автор и взял половину «заметок».
Я конечно не специалист, но разве у неупакованных ссылочных типов есть индекс блока синхронизации? По-моему как раз у них его и нет.

Вы скорее всего опечатались, механизм упаковки/распаковки существует только для value type.
У структурных типов и примитивных (byte,int,long...) нет блока синхронизации

У value type нет слова заголовка объекта, у reference type (в том числе упакованных value type) он есть — это основа основ, которую, я думал, знают все.
Исключения медленно работают, т.к. происходит переход в режим ядра.

Формально да, это так. Хотя определение «медленно» для разных задач разное, так что нужно просто сказать что происходит переключение контекста потока.
Исключения медленно работают, т.к. происходит переход в режим ядра.

Формально да, это так. Хотя определение «медленно» для разных задач разное, так что нужно просто сказать что происходит переключение контекста потока.
При обработке исключения происходит серия переходов managed-unmanaged. Но сами такие переходы не страшны. К примеру, по два таких перехода происходит на каждый вызов делегата, если верить отладчику студии. Означает ли это, что делегатов надо избегать?

Исключения работают медленно, главным образом, из-за сбора StackTrace. Чтобы не потерять информацию, стек надо собрать еще перед переходом в блок catch — то есть в тот момент, когда не ясно, понадобится ли он вообще.

Но при чем тут вообще режим ядра?
Но при чем тут вообще режим ядра?

На сколько я знаю механизм обработки исключений в windows, требует перевода процессора в режим ядра (kernel mode), т.к. диспетчер исключений обращается системной памяти.
А по поводу делегатов я к сожалению не могу ничего сказать, я не знаю как они работаю внутри, но на сколько я понимаю делегаты не требуют обращения за пределы виртуальной памяти процесса, поэтому для их работы перевод процессора в режим ядра врятли происходит.
Э… зачем? Какая секретная информация хранится в системной памяти?
Некоторые исключения система обрабатывает сама, собственно обработчик хранится в системной памяти.
Например используемый при отладке breakpoint вызывает исключение, которое ОС обрабатывает вызывая отладчик.
Вы сейчас путаете аппаратные исключения, то есть прерывания, сгенерированные самим процессором — и исключения языков программирования, которые обрабатываются целиком в пользовательском режиме.
Я их не путаю, у аппаратных исключений в отличие от программных просто другой источник, но механизм обработки в общем-то такой же.
Я не большой специалист в этих вопросах и видимо моих знаний недостаточно чтобы объяснить этот механизм. За более подробной информацией могу посоветовать посмотреть книгу Windows Internals глава Диспечеризация исключений.
Не знаю, что в этой книге понимается под программными исключениями — но в этой главе пишется про исключительные ситуации, генерируемые железом. Про исключения, генерируемые вот так: throw new Exception() — там нет ни слова.
>>Я не большой специалист в этих вопросах
Это видно. Эксепшны в дотнете сугубо user-mode. И да, в процессоре нет kernel mode. Там ring0.
Прочитал ваше сообщение, даже засомневался, отыскал в книге этот пункт, нашел в статью в msdn, как еще одно подтверждение перехода в режим ядра.

1) "Я говорил об ущербе производительности, потому что, несмотря на свободу исключений в .NET, внутри они реализуются через SEH. Если хотите это проверить, отладьте приложение .NET, используя отладку в неуправляемом режиме (native mode–only debugging), — вы увидите те же первые случаи исключения при инициации вашего исключения. Это подразумевает переход в режим ядра при каждом исключении. Идея, повторяю, в том, чтобы инициировать исключения при ошибках, а не в нормальном ходе выполнения программы."

2) «Actually, .NET exceptions are implemented in terms of SEH exceptions, which means that they do go on a trip through kernel mode. See blogs.msdn.com/cbrumme/archive/2003/10/01/51524.aspx3 for Chris Brumme's explanation.»

С такими отзывами, я раз 10 перечитаю эти заметки, спасибо!
Нашел эту статью в открытом доступе (hint: надо стереть тройку в конце ссылки). Сделал поиск по ключевому слову «kernel». Не нашел утверждений о том, что любое исключение SEH безусловно проходит через ядро.

Вот самое близкое из найденного:
Probably take a trip through the OS kernel. Often take a hardware exception.
Но тут говорится о возможном проходе через ядро — в случае использования отладчиков, профайлеров и прочих инструментов, которые при нормальной работе приложения отключены.

Откуда вообще пошло гулять по инету утверждение о том, что любое исключение SEH проходит через ядро ОС?
В книге могла быть ошибка перевода. Спорные моменты требуют нескольких независимых источников.
Блог надо прочитать целиком, не Испанский ведь.
Лично мне достаточно других источников. Не помню название, но была какая-то переведенная книга именно про SEH. Стоит сейчас где-то в шкафу у бабушки.

Если кто-нибудь найдет в первоисточниках информацию, опровергающую мои утверждения — буду благодарен. Пока что доказательств я не вижу (приведенная в пункте 2 цитата — не первоисточник, потому что сама ссылается на другой источник).
Самое смешное в этом диалоге то что мы в поисках ответа не подумали о том что бы запустить отладчик поддерживающий ring 0 и проверить это собственными руками, а не ссылаться вслепую на авторитетов.
Достаточно отладчика, поддерживающего перехват системных вызовов: не требуется отлаживать ядро, достаточно зафиксировать факт обращения к нему (ну и, конечно же, надо потом как-то проверить, что это обращение не было вызвано самим фактом отладки).

К сожалению, я не знаю ни одного отладчика, кроме отладчика Студии. И даже в отладчике Студии не знаю кнопки, позволяющей хотя бы переключиться в режим дизассемблера (обычно у меня стоит обратная задача — выключить этот чертов режим, так так, чтобы он не включался автоматически). Но это не означает, что я не подумал о проверке! Просто результатом этих дум был вывод о том, что для меня такой повод сейчас непосилен :)
WinDbg, IDA.
Вот еще ссылка в тему blog.codinghorror.com/understanding-user-and-kernel-mode.

написал простой тест — в бесконечном цикле создаем исключение/пробрасываем/ловим — в ProcessExplorer-е
Kernel Time стабильно растет. Если в тесте оставить только создание класса исключения — Kernel Time не изменяется (win7, x64).
Здорово.
еще немного поиcследовал вопрос:
1. в предыдущем эксперименте у меня стояла галочка Prefer32Bit, т.е. процесс из под wow64 был запущен. Если ее убрать, то время в ядре становится меньше гораздо, но все равно с работой программы продолжает расти
2. по стекам ProcessExplorer-а видно, что throw замыкается на RaiseException — стандартную
API функцию для генерации SEH-вских эксепшнов.
3. обработка SEH-вских фреймов (как и описано в вышеприведенных статьях) происходит в два
этапа — вначале RtlDispatchException ищет обработчик выше по стеку, затем этот обработчик вызывает RtlUnwind для снятия со стека всего лишнего (EXCEPTION_REGISTRATION-ов/активаций функций).
4. судя по частично доступным в сети сорцам винды 2000, единственное место, которое обращается
в ядро в этой схеме — это переход из RtlUnwind на найденный фрейм через системный сервис ZwContinue.
Есть WRK v1.2 (официально доступный исходник)
The Windows Research Kernel v1.2 contains the sources for the core of
the Windows (NTOS) kernel and a build environment for a kernel that will run on
x86 (Windows Server 2003 Service Pack 1) and
amd64 (Windows XP x64 Professional)
A future version may also support booting WRK kernels on Windows XP x86 systems,
but the current kernels will fail to boot due to differences in some shared
structures.

The NTOS kernel implements the basic OS functions
for processes, threads, virtual memory and cache managers, I/O management,
the registry, executive functions such as the kernel heap and synchronization,
the object manager, the local procedure call mechanism, the security reference
monitor, low-level CPU management (thread scheduling, Asynchronous and Deferred
Procedure calls, interrupt/trap handling, exceptions), etc.

Смотрел по курсу Intuit.ru «Введение во внутреннее устройство Windows».
Очень интересно, правда забыл уже почти все.
Кто такие «неупакованные ссылочные типы»? :) Наверное, речь шла о неупакованных значимых типах. Разумеется, у них такого индекса нет. Но такой индекс есть, к примеру, у упакованных значимых типов!

Но вообще-то, когда я писал свой комментарий, я думал совсем о другом. Дело в том, что string — это тоже примитивный тип! Но будет ли кто-то спорить с тем, что у него есть все необходимое для синхронизации?
mayorovp, a_mastrakov
Да. Я очепятался. Правда очень грубый косяк. К вечеру на работе уже голова болит. Я и тот комментарий не с первого раза смог сформулировать. Несколько раз переписывал.
Дело в том, что string — это тоже примитивный тип!

Я может быть чего-то не знаю — но всегда считал string — указательный неизменяемый тип. Так по крайней мере говорится в MSDN: https://msdn.microsoft.com/en-us/library/system.type.isprimitive(v=vs.100).aspx.
Примитивный тип — это тип, который не может быть описан через другие типы. У типа string существует одна возможность, которая не может быть реализована иначе как средствами CLR — это получение строкового объекта исходя из строкового литерала.
А почему string нельзя описать через другие типы? Один int для длинны и массив char. Собственно в CLR тип string так и реализован.
Представим, что строкового типа у нас нет. Что нужно сделать, чтобы следующая строка кода заработала?

MyString foo = "Hello, world!";
1) У структурных типов и примитивных (byte,int,long...) нет блока синхронизации, который присутствует у объектов в управляемой куче на ряду с ссылкой.
Если не читать то что после запятой, то да, такое допущение будет ошибочным.

2) Методы расширения — прочитайте паттерн Visitor в бесплатной книге itvdn.com/ru/patterns (которая идет как дополнение к первоисточнику GOF).
Я не сам это придумал, это ведь заметки а не мои домыслы.
1) У структурных типов и примитивных (byte,int,long...) нет блока синхронизации, который присутствует у объектов в управляемой куче на ряду с ссылкой.
Если не читать то что после запятой, то да, такое допущение будет ошибочным.
Не вижу разницы, если честно.

2) Методы расширения — прочитайте паттерн Visitor в бесплатной книге itvdn.com/ru/patterns (которая идет как дополнение к первоисточнику GOF).
Я не сам это придумал, это ведь заметки а не мои домыслы.
Спасибо, я знаю что такое паттерн Visitor. Не вижу, как в его реализации могут помочь методы расширения, которые являются всего лишь обычными статическими методами.
1) Enum структурный тип, но не примитивный. Согласен что предложение не самое удачное (спасибо что указали), но это не делает его не правильным.

2) Цитата из бесплатной книги (ссылка на нее выше):
«Паттерн Visitor – позволяет единообразно обойти набор элементов с разнородными интерфейсами (т.е. набор объектов разных классов не приводя их к общему базовому типу), а также позволяет добавить новый метод (функцию) в класс объекта, при этом не изменяя сам класс этого объекта.»
Я не имею такой же квалификации как автор этих слов, но согласен с ним.
Тот факт, что метод расширения иногда решает ту же самую задачу, которую можно было бы решить паттерном Visitor, не делает из него реализацию этого паттерна.
Цитата из той же книги:
«Известные применения паттерна в .Net

Паттерн Visitor, выражен в платформе .Net в виде идеи использования расширяющих методов.»

По моему скромному мнению нет смысла спорить о теплом и твердом. Но раз вы считаете иначе, возможно так и есть.
Сожгите эту книгу!
> Паттерн Visitor… позволяет добавить новый метод (функцию) в класс объекта, при этом не изменяя сам класс этого объекта.

Добавить новый _полимофный_ метод в класс объекта. Extension-методы — это просто синтаксический сахар (языковой, не CLR) для _вызова_ функций. Языки, где нет этого сахара, просто используют другой синтаксис вызова. Паттерн Visitor к синтаксису того или иного языка не имеет никакого отношения. К объекту какого класса будет применён extension-метод — это определяется во время компиляции. К объекту какого класса будет применятся навешенный с помощью Visitor'а «метод» — это определяется во время исполнения, так как в compile time этой информации в общем случае нет.
Структура передает свою копию в метод, Класс передает копию своей ссылки. А вот когда мы используем ключевое слово REF — структура передает указатель на себя, а класс передает свою исходную ссылку.

А я всегда считал что ref для ссылочных типов передает указатель на ссылку на объект.
А я вообще считал, что параметры передает в метод вызывающий метод, а не они сами…
> В то же время, объявляя тип возвращаемого методом объекта, желательно выбирать самый сильный из доступных вариантов (пытаясь не ограничиваться конкретным типом). Например, лучше объявлять метод, возвращающий объект FileStream, а не Stream.

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

Например, пользователь вызывает ваш код:
`FileStream hisObject = MyApi.GetStream();`
Вы решили, что ваша реализация GetStream может возвращать не только FileStream, но и MemoryStream, так что вы изменили возвращаемое значение на Stream — и у пользователя сломается код. Этого не было бы проблемой, если бы вы изначально не обещали большего, чем Stream.

Наоборот, если вы начинали с сигнатуры `MyApi.GetStream(): Stream`, а потом решили специфицировать более конкретным типом (и заморозить его в API) и дать больший контроль пользователю, то изменение сигнатуры не сломает уже существующий клиентский код.
В C# внутри типов, помеченных атрибутом [Serializable], не стоит определять автоматически реализуемые свойства. Дело в том, что имена полей, генерируемые компилятором, могут меняться после каждой следующей компиляции, что сделает невозможной десериализацию экземпляров типа.
Нашел эту цитату у Рихтера. Но не пойму, насколько и в каких случаях это может быть проблемой? За сколько-то лет работы ни разу с такой ошибкой не сталкивался. Куча сериализуемых классов.
Результат тестов.
Класс:
[Serializable]
class Person
{
     string FirstName { get; set; }
     string SecondName { get; set; }
     int Age { get; set; }
}

ILDASM.exe

Debug (first compile):
.field private int32 '<Age>k__BackingField'
.field private string '<FirstName>k__BackingField'
.field private string '<SecondName>k__BackingField'

Debug (second compile):
.field private int32 '<Age>k__BackingField'
.field private string '<FirstName>k__BackingField'
.field private string '<SecondName>k__BackingField'

Release (third compile):
.field private int32 '<Age>k__BackingField'
.field private string '<FirstName>k__BackingField'
.field private string '<SecondName>k__BackingField'


Имена остались прежними, но возможно Джеффри Рихтер встречал другие ситуации (иначе зачем он обратил на это внимание) или в компиляторе были изменения. У меня VS 2013, летом на 2015 :)
Да это-то понятно. Один и тот же компилятор генерирует одни и те же имена… Интереснее было бы сравнить разные компиляторы.
Проверил пункт «36) Поля класса».

1) Неправильно.
class Car
    {
        private string _name = "car";
        private int _speed = 15;
        private int _sits = 5;

        public Car()
        {

        }

        public Car(string name)
        {
            this._name = name;
        }

        public Car(int speed)
        {
            this._speed = speed;
        }

        public Car(int speed ,int sits)
        {
            this._speed = speed;
            this._sits = sits;
        }
    }

  // Code size       43 (0x2b)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldstr      "car"
  IL_0006:  stfld      string ConsoleApplication1.Car::_name
  IL_000b:  ldarg.0
  IL_000c:  ldc.i4.s   15
  IL_000e:  stfld      int32 ConsoleApplication1.Car::_speed
  IL_0013:  ldarg.0
  IL_0014:  ldc.i4.5
  IL_0015:  stfld      int32 ConsoleApplication1.Car::_sits
  IL_001a:  ldarg.0
  IL_001b:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0020:  nop
  IL_0021:  nop
  IL_0022:  ldarg.0
  IL_0023:  ldarg.1
  IL_0024:  stfld      string ConsoleApplication1.Car::_name
  IL_0029:  nop
  IL_002a:  ret
} // end of method Car::.ctor

  // Code size       43 (0x2b)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldstr      "car"
  IL_0006:  stfld      string ConsoleApplication1.Car::_name
  IL_000b:  ldarg.0
  IL_000c:  ldc.i4.s   15
  IL_000e:  stfld      int32 ConsoleApplication1.Car::_speed
  IL_0013:  ldarg.0
  IL_0014:  ldc.i4.5
  IL_0015:  stfld      int32 ConsoleApplication1.Car::_sits
  IL_001a:  ldarg.0
  IL_001b:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0020:  nop
  IL_0021:  nop
  IL_0022:  ldarg.0
  IL_0023:  ldarg.1
  IL_0024:  stfld      int32 ConsoleApplication1.Car::_speed
  IL_0029:  nop
  IL_002a:  ret
} // end of method Car::.ctor

  // Code size       50 (0x32)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldstr      "car"
  IL_0006:  stfld      string ConsoleApplication1.Car::_name
  IL_000b:  ldarg.0
  IL_000c:  ldc.i4.s   15
  IL_000e:  stfld      int32 ConsoleApplication1.Car::_speed
  IL_0013:  ldarg.0
  IL_0014:  ldc.i4.5
  IL_0015:  stfld      int32 ConsoleApplication1.Car::_sits
  IL_001a:  ldarg.0
  IL_001b:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0020:  nop
  IL_0021:  nop
  IL_0022:  ldarg.0
  IL_0023:  ldarg.1
  IL_0024:  stfld      int32 ConsoleApplication1.Car::_speed
  IL_0029:  ldarg.0
  IL_002a:  ldarg.2
  IL_002b:  stfld      int32 ConsoleApplication1.Car::_sits
  IL_0030:  nop
  IL_0031:  ret
} // end of method Car::.ctor


2) Правильно.
 class Car
    {
        private string _name;
        private int _speed;
        private int _sits;

        public Car()
        {
            this._name = "car";
            this._speed = 15;
            this._sits = 5;
        }

        public Car(string name) : this()
        {
            this._name = name;
        }

        public Car(int speed) : this()
        {
            this._speed = speed;
        }

        public Car(int speed ,int sits) : this()
        {
            this._speed = speed;
            this._sits = sits;
        }
    }

  // Code size       17 (0x11)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       instance void ConsoleApplication1.Car::.ctor()
  IL_0006:  nop
  IL_0007:  nop
  IL_0008:  ldarg.0
  IL_0009:  ldarg.1
  IL_000a:  stfld      string ConsoleApplication1.Car::_name
  IL_000f:  nop
  IL_0010:  ret
} // end of method Car::.ctor

  // Code size       17 (0x11)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       instance void ConsoleApplication1.Car::.ctor()
  IL_0006:  nop
  IL_0007:  nop
  IL_0008:  ldarg.0
  IL_0009:  ldarg.1
  IL_000a:  stfld      int32 ConsoleApplication1.Car::_speed
  IL_000f:  nop
  IL_0010:  ret
} // end of method Car::.ctor

  // Code size       24 (0x18)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       instance void ConsoleApplication1.Car::.ctor()
  IL_0006:  nop
  IL_0007:  nop
  IL_0008:  ldarg.0
  IL_0009:  ldarg.1
  IL_000a:  stfld      int32 ConsoleApplication1.Car::_speed
  IL_000f:  ldarg.0
  IL_0010:  ldarg.2
  IL_0011:  stfld      int32 ConsoleApplication1.Car::_sits
  IL_0016:  nop
  IL_0017:  ret
} // end of method Car::.ctor


А так если у кого есть желание и VS не 2013, пусть выложат свои результаты по автосвойствам.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории