CLR via C#

CLR via C#Давеча закончил читать прекрасную книгу по .NET/C# от тов. Рихтера – “CLR via C#”.
Книга – отличный экскурс в глубины шарпа и CLR. Стоит заметить, Рихтер увлекается копанием в дебрях, любит экономить на спичках и расписывать как это делает. Отсюда и толщина книженции, из которой не напрягаясь можно половину выкинуть. Тем не менее, книга отличная и читать стоит.

Ниже идет список заметок, которые сделал во время чтения.

  • На что влияет выбор платформы назначения при компиляции:clr_platforms
  • Для ускорения запуска приложения можно заранее скомпилировать его в машинный код целевой платформы. Можно делать в установщике. К примеру, так делает “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:
    win32_ex
  • Перед вызовом метода можно воспользоваться методом EnsureSufficientExecutionStack класса RuntimeHelper для проверки количества свободного места в стеке для вызова «среднего» метода (который не имеет четкого определения). Если места в стеке недостаточно, метод генерирует исключение InsufficientExecutionStackException, которое вы можете перехватить.
  • Автономное приложение может приказать CLR использовать серверный уборщик мусора.
  • Прибегая к параллельной уборке мусора, приложение обычно расходует больше памяти, чем при непараллельной уборке.
  • Приложение может контролировать уборку мусора при помощи свойства GCLatencyMode класса GCSettings:
    gc_mode
  • В ходе установки .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, что приводит к установлению неявно открытой блокировки.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *