Давеча закончил читать прекрасную книгу по .NET/C# от тов. Рихтера – “CLR via C#”.
Книга – отличный экскурс в глубины шарпа и CLR. Стоит заметить, Рихтер увлекается копанием в дебрях, любит экономить на спичках и расписывать как это делает. Отсюда и толщина книженции, из которой не напрягаясь можно половину выкинуть. Тем не менее, книга отличная и читать стоит.
Ниже идет список заметок, которые сделал во время чтения.
- На что влияет выбор платформы назначения при компиляции:
- Для ускорения запуска приложения можно заранее скомпилировать его в машинный код целевой платформы. Можно делать в установщике. К примеру, так делает “Paint .NET”.
- Для MSSQL, IBM DB2 и Oracle можно писать хранимые процедуры на C#.
- Поиск сборок идет в следующем порядке (CLR автоматом проверяет подкаталог, совпадающий с именем сборки):
AppDir\AsmName.dll
AppDir\AsmName\AsmName.dll
AppDir\firstPrivatePath\AsmName.dll
AppDir\firstPrivatePath\AsmName\AsmName.dll
AppDir\secondPrivatePath\AsmName.dll
AppDir\secondPrivatePath\AsmName\AsmName.dl
Если назначен региональный стандарт, то в таком:
C:\AppDir\en US\AsmName.exe
C:\AppDir\en US\AsmName\AsmName.exe
C:\AppDir\firstPrivatePath\en US\AsmName.exe
C:\AppDir\firstPrivatePath\en US\AsmName\AsmName.exe
C:\AppDir\secondPrivatePath\en US\AsmName.exe
C:\AppDir\secondPrivatePath\en US\AsmName\AsmName.exe
C:\AppDir\en\AsmName.dll
C:\AppDir\en\AsmName\AsmName.dll
C:\AppDir\firstPrivatePath\en\AsmName.dll
C:\AppDir\firstPrivatePath\en\AsmName\AsmName.dll
C:\AppDir\secondPrivatePath\en\AsmName.dll
C:\AppDir\secondPrivatePath\en\AsmName\AsmName.dll - Ни в коем случае нельзя копировать в GAC (Global Assembly Cache) файлы сборок вручную. Для этого следует использовать GACUtil.exe.
- В .NET Framework все файлы сборок, созданных Microsoft, устанавливаются в двух экземплярах. Один набор файлов заносится в один каталог с CLR, а другой — в каталог GAC. Файлы в каталоге CLR облегчают построение пользовательских сборок, а их копии в GAC предназначены для загрузки во время выполнения.
- В приложении можно зарегистрировать специальные колбэки, решающие проблемы с поиском сборок и типов:
System.AppDomain.AssemblyResolve
System.AppDomain.ReflectionOnlyAssemblyRessolve
System AppDomain.TypeResolve - CLR поддерживает перемещение типов между сборками для сохранения обратной совместимости. С этой целью, в оригинальной сборке к типу применяется атрибут TypeForwardedToAttribute.
- Различные типы, принадлежащие одному пространству имен, могут быть реализованы в разных сборках.
- Нельзя распаковать объект и одновременно привести к другому типу. Например, так не выйдет:
Int32 x = 5; Object o = x; Int16 y = (Int16) o;
А в две операции можно:
Int16 y = (Int16)(Int32)o;
- Если после создания значимого типа не вызывать методы, изменяющие его состояние, не возникнет недоразумений при копировании поля в процессе упаковки и распаковки.
- При десериализации объекта конструктор, обычно, не вызывается.
- Пример метода перегруженного оператора:
public sealed class Complex { public static Complex operator+(Complex c1, Complex c2) { // ... } }
- При отладке блока catch в Microsoft Visual Studio для просмотра текущего исключения следует добавить в окно контрольных значений специальную переменную $exception.
- Способы получения информации о необработанных исключениях в разных моделях приложений:
- Для многих приложений — событие UnhandledException класса System.AppDomain. Приложения Windows Store и Microsoft Silverlight не могут обращаться к этому событию.
- Для приложений Windows Store — события UnhandledException класса Windows.UI.Xaml.Application.
- Для приложений Windows Forms — виртуальный метод OnThreadException класса System.Windows.Forms.NativeWindow, одноименный виртуальный метод класса System.Windows.Forms.Application и событие ThreadException класса System.Windows.Forms.Application.
- Для приложений Windows Presentation Foundation (WPF) — событие DispatcherUnhandledException класса System.Windows.Application, а также события UnhandledException и UnhandledExceptionFilter класса System.Windows.Threading.Dispatcher.
- Для приложений Silverlight — событие UnhandledException класса System.Windows.Application.
- Для приложений ASP.NET Web Form — событие Error класса System.Web.UI.TemplateControl. Класс TemplateControl — базовый для System.Web.UI.Page и System.Web.UI.UserControl. Кроме того, можно задействовать событие Error класса System.Web.HTTPApplication.
- Для приложений Windows Communication Foundation — свойство ErrorHandlers класса System.ServiceModel.Dispatcher.ChannelDispatcher.
- В CLR некоторые исключения, генерируемые машинным кодом, рассматриваются как исключения поврежденного состояния (Corrupted State Exceptions, CSE). По умолчанию CLR не позволяет управляемому коду перехватывать такие исключения и блок finally не выполняется. Вот список CSE-исключений в Win32:
- Перед вызовом метода можно воспользоваться методом EnsureSufficientExecutionStack класса RuntimeHelper для проверки количества свободного места в стеке для вызова «среднего» метода (который не имеет четкого определения). Если места в стеке недостаточно, метод генерирует исключение InsufficientExecutionStackException, которое вы можете перехватить.
- Автономное приложение может приказать CLR использовать серверный уборщик мусора.
- Прибегая к параллельной уборке мусора, приложение обычно расходует больше памяти, чем при непараллельной уборке.
- Приложение может контролировать уборку мусора при помощи свойства GCLatencyMode класса GCSettings:
- В ходе установки .NET Framework устанавливается также набор счетчиков производительности, которые позволяют собирать в реальном времени самые разнообразные статистические данные о CLR. Эти данные можно просматривать с помощью утилиты PerfMon exeили системного монитора из состава Windows.
- При повторных вызовах методы Dispose никогда не должны выдавать исключение ObjectDisposedException — они должны просто возвращать управление.
- В .NET Framework поддерживаются управляемые расширения отладки (Managed Debugging Assistants, MDA). Когда они включены, .NET Framework выполняет поиск некоторых распространенных ошибок в программах и запускает соответствующий отладчик. В отладчике все это выглядит как генерирование исключения MDA умеет определять ситуации, когда объект StreamWriter удален уборщиком мусора до своего закрытия. Чтобы включить данный управляемый отладчик в Visual Studio, откройте проект и выберите в меню команду Debug > Exceptions. В диалоговом окне Exceptions раскройте узел Managed Debugging Assistants, прокрутите страницу вниз до элемента StreamWriterBufferedDataLost и установите флажок Thrown, чтобы заставить отладчик Visual Studio останавливаться при каждой потере данных объекта StreamWriter.
- Можно сообщить уборщику мусора, что объект скрытно (например, через неуправляемый код) использует много памяти методами:
public static void AddMemoryPressure(Int64 bytesAllocated); public static void RemoveMemoryPressure(Int64 bytesAllocated);
- Замечательная особенность ASP.NET — возможность изменять код веб-сайта без остановки веб-сервера. Когда файл на жестком диске сайта меняется, ASP.NET обнаруживает это, выгружает домен, содержащий старую версию (после завершения текущего запроса), а затем создает новый домен, загружая в него новые версии файлов. При этом ASP.NET использует особую функцию доменов, называемую теневым копированием (shadow copying).
- Загрузка сборки из встроенного ресурса:
private static Assembly ResolveEventHandler(Object sender, ResolveEventArgs args) { String dllName = new AssemblyName(args.Name).Name + ".dll"; var assem = Assembly.GetExecutingAssembly(); String resourceName = assem .GetManifestResourceNames() .FirstOrDefault(rn =>rn.EndsWith(dllName)); if (resourceName == null) return null; // Not found, maybe another handler will find it using (var stream = assem.GetManifestResourceStream(resourceName)) { Byte[] assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } }
- Microsoft использует нотацию Бэкуса–Наура для записи имен типов и имен с указанием сборки, которые используются для написания строк, передаваемых в методы отражения.
- Полезный метод, использующий сериализацию для создания глубокой копии (клона) объекта:
private static Object DeepClone(Object original) { // Создание временного потока в памяти using (var stream = new MemoryStream()) { // Создания модуля форматирования для сериализации var formatter = new BinaryFormatter(); // Эта строка описывается в разделе "Контексты потока ввода-вывода" formatter.Context = new StreamingContext(StreamingContextStates.Clone); // Сериализация графа объекта в поток в памяти formatter.Serialize(stream, original); // Возвращение к началу потока в памяти перед десериализацей stream.Position = 0; // Десериализация графа в новый набор объектов и возвращение // корня графа (детальной копии) вызывающему методу return formatter.Deserialize(stream); } }
- CLR гарантирует атомарность чтения и записи следующих типов данных: Boolean, Char, (S)Byte, (U)Int16, (U)Int32, (U)IntPtr, Singleи ссылочных типов. Для (U)Int64 уже не гарантируется.
- Разрешаем использовать только одну копию приложения:
public static class Program { public static void Main() { Boolean createdNew; // Пытаемся создать объект ядра с указанным именем using (new Semaphore(0, 1, "SomeUniqueStringIdentifyingMyApp",out createdNew)) { if (createdNew) { // Этот поток создает ядро, так что другие копии приложения // не могут запускаться. Выполняем остальную часть приложения... } else { // Этот поток открывает существующее ядро с тем же именем; // должна запуститься другая копия приложения. // Ничего не делаем, ждем возвращения управления от метода Main, // чтобы завершить вторую копию приложения } } } }
- Существует класс ManualResetEventSlim, который отличается от ManualResetEvent тем, что поначалу не переходит в режим ядра, а выполняет ожидание в цикле. Полезно, если время ожидание короткое.
- Применение к методу атрибута [MethodImpl(MethodImplOptions.Synchronized)] заставляет JIT-компилятор окружить машинный код метода вызовами Monitor.Enter и Monitor.Exit. Если метод является экземплярным, этим методам передается this, что приводит к установлению неявно открытой блокировки.