Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
На протяжении всей своей истории .NET старалась поддерживать высокий уровень совместимости от версии к версии и между реализациями .NET. Хотя .NET 5 (и .NET Core) и более поздних версий можно рассматривать как новую технологию по сравнению с .NET Framework, два основных фактора ограничивают способность этой реализации .NET отходить от .NET Framework:
- Большое количество разработчиков либо изначально разработали, либо продолжают разрабатывать приложения на платформе .NET Framework. Они ожидают согласованного поведения во всех реализациях .NET.
- .NET проекты библиотеки Standard позволяют разработчикам создавать библиотеки, нацеленные на общие API, которые используются в .NET Framework, .NET 5 (и .NET Core), а также в более поздних версиях. Разработчики ожидают, что библиотека, используемая в приложении .NET, должна вести себя идентично той же библиотеке, используемой в приложении .NET Framework.
Наряду с совместимостью в различных реализациях .NET разработчики ожидают высокий уровень совместимости между версиями конкретной реализации .NET. В частности, код, написанный для более ранней версии .NET Core, должен работать без проблем на .NET 5 или более поздней версии. На самом деле, многие разработчики ожидают, что новые API, найденные в недавно выпущенных версиях .NET, также должны быть совместимы с предварительной версией, в которой были представлены эти API.
В этой статье описываются изменения, влияющие на совместимость и способ оценки каждого типа изменений в команде .NET. Понимание того, как команда разработчиков .NET подходит к возможным критическим изменениям, особенно полезно для разработчиков, которые открывают пулл-реквесты, изменяющие поведение существующих .NET API.
В следующих разделах описываются категории изменений, внесенных в .NET API и их влияние на совместимость приложений. Изменения разрешены (✔️), запрещены (❌) или требуют решения и оценки того, насколько прогнозируемым, очевидным и согласованным было предыдущее поведение (❓).
Замечание
- Помимо того, как оцениваются изменения библиотек .NET, разработчики библиотек также могут использовать эти критерии для оценки изменений в своих библиотеках, предназначенных для нескольких реализаций и версий .NET.
- Сведения о категориях совместимости, например прямой и обратной совместимости, см. в разделе Как изменения кода могут повлиять на совместимость.
Изменения в публичном контракте
Изменения в этой категории изменяют общедоступную область поверхности типа. Большинство изменений в этой категории запрещены, так как они нарушают обратную совместимость (возможность приложения, разработанного с предыдущей версией API, выполнять без повторной компиляции в более поздней версии).
Типы
✔️ РАЗРЕШЕНО: удаление реализации интерфейса из типа, когда интерфейс уже реализован базовым типом
❓ ТРЕБУЕТСЯ РЕШЕНИЕ. Добавление новой реализации интерфейса в тип
Это приемлемое изменение, так как оно не влияет на существующие клиенты. Любые изменения типа должны работать в пределах допустимых изменений, определенных здесь, чтобы новая реализация оставалась приемлемой. Крайняя осторожность необходима при добавлении интерфейсов, которые напрямую влияют на способность конструктора или сериализатора создавать код или данные, которые не могут быть использованы на нижнем уровне. Примером является ISerializable интерфейс.
❓ ТРЕБУЕТСЯ ОЦЕНКА: Представление нового базового класса
Тип может быть введен в иерархию между двумя существующими типами, если он не вводит новые абстрактные члены или изменяет семантику или поведение существующих типов. Например, в .NET Framework 2.0 класс DbConnection стал новым базовым классом для SqlConnection, который ранее был получен непосредственно от Component.
✔️ РАЗРЕШЕНО. Перемещение типа из одной сборки в другую
Старая сборка должна быть помечена темTypeForwardedToAttribute, что указывает на новую сборку.
✔️ РАЗРЕШЕНО: изменение типа структуры на
readonly structтипИзменение типа
readonly structна типstructне допускается.✔️ РАЗРЕШЕНО. Добавление запечатаемого или абстрактного ключевого слова в тип, если нет доступных (общедоступных или защищенных) конструкторов
✔️ РАЗРЕШЕНО: расширение видимости типа
❌ ЗАПРЕЩАЕТСЯ: Изменение пространства имен или наименования типа
❌ ЗАПРЕЩЕНО: переименование или удаление общедоступного типа
Это нарушает весь код, использующий переименованный или удаленный тип.
Замечание
В редких случаях .NET может удалить общедоступный API. Дополнительные сведения см. в статье об удалении API в .NET. Сведения о политике поддержки .NET см. в разделе .NET Политика поддержки.
❌ DISALLOWED: изменение базового типа перечисления
Это изменения, нарушающие время компиляции и поведение, а также нарушающее двоичное изменение, которое может сделать аргументы атрибутов неподлежащими разбору.
❌ ЗАПРЕЩЕНО: Запечатывание типа, который ранее был незапечатан
❌ DISALLOWED: добавление интерфейса в группу базовых типов интерфейса
Если интерфейс реализует интерфейс, который он ранее не реализовал, все типы, реализующие исходную версию интерфейса, разбиваются.
❓ ТРЕБУЕТСЯ РЕШЕНИЕ. Удаление класса из набора базовых классов или интерфейса из набора реализованных интерфейсов
Существует одно исключение для удаления интерфейса: можно добавить реализацию интерфейса, наследуемого от удаленного интерфейса. Например, можно удалить IDisposable, если тип или интерфейс теперь реализует IComponent, который реализует IDisposable.
❌ Запрещено: изменение
readonly structтипа на структуруОднако допускается изменение
structтипа на типreadonly struct.❌ DISALLOWED: изменение типа struct на
ref structтип и наоборот❌ ЗАПРЕЩЕНО: уменьшение видимости типа
Однако увеличение видимости типа допускается.
Члены
✔️ РАЗРЕШЕНО. Расширение видимости элемента, который не является виртуальным
✔️ РАЗРЕШЕНО: добавление абстрактного элемента в открытый тип, не имеющий доступных (общедоступных или защищенных) конструкторов, или тип запечатан.
Однако добавление абстрактного элемента в тип с доступными (общедоступными или защищенными) конструкторами и не
sealedне допускается.✔️ РАЗРЕШЕНО. Ограничение видимости защищенного элемента, если тип не имеет доступных (общедоступных или защищенных ) конструкторов или тип запечатан.
✔️ РАЗРЕШЕНО. Перемещение члена в класс выше в иерархии, чем тип, из которого он был удален
✔️ РАЗРЕШЕНО: Добавление или удаление переопределения
Введение переопределения может привести к тому, что предыдущие потребители пропускают переопределение при вызове базы.
✔️ РАЗРЕШЕНО. Добавление конструктора в класс вместе с конструктором без параметров, если у класса ранее не было конструкторов
Однако добавление конструктора в класс, который ранее не имел конструктора, без добавления конструктора без параметров, не допускается.
✔️ РАЗРЕШЕНО: изменение элемента с абстрактного на виртуальный
✔️ РАЗРЕШЕНО: изменение возвращаемого значения с
ref readonlyнаref(за исключением виртуальных методов или интерфейсов)✔️ РАЗРЕШЕНО: удаление только для чтения из поля, за исключением случаев, когда статический тип поля является изменяемым типом значения
✔️ РАЗРЕШЕНО: вызов нового события, которое ранее не было определено
❓ ТРЕБУЕТСЯ РЕШЕНИЕ. Добавление нового поля экземпляра в тип
Это изменение влияет на сериализацию.
❌ DISALLOWED: переименование или удаление публичного элемента или параметра
Это нарушает весь код, использующий переименованный или удаленный член или параметр.
Это включает удаление или переименование метода получения или задания из свойства, а также переименование или удаление элементов перечисления.
❓ ТРЕБУЕТСЯ ОЦЕНКА: Добавление члена в интерфейс
Хотя это является критическим изменением в том смысле, что минимальная версия .NET увеличивается до .NET Core 3.0 (C# 8.0), когда были введены default-члены интерфейсов (DIMs), добавление статического, не абстрактного и не виртуального члена в интерфейс разрешено.
Если вы предоставляете реализацию, добавление нового члена в существующий интерфейс не обязательно приведет к сбоям компиляции в подчиненных сборках. Однако не все языки поддерживают DIM. Кроме того, в некоторых сценариях среда выполнения не может решить, какой член интерфейса по умолчанию должен вызываться. Начиная с C# 13
ref structтипы могут реализовывать интерфейсы, но их нельзя упаковать или преобразовать в тип интерфейса. Таким образом, типref structдолжен предоставлять явную реализацию для каждого элемента интерфейса экземпляра — он не может использовать реализацию по умолчанию, предложенную интерфейсом. Добавление элемента экземпляра по умолчанию в интерфейс, которыйref structреализует, требует отref structдобавления соответствующей реализации, что является критическим изменением исходного кода. По этим причинам проявляйте осторожность при добавлении члена в уже существующий интерфейс.Замечание
Если интерфейс реализован типами
ref struct(возможно, в C# 13 и более поздних версиях), добавление любого члена экземпляра по умолчанию в интерфейс является изменением, нарушающим исходный код для вызывающих кодов. Необходимо предоставить явную реализацию нового члена. Онаref structне может вернуться к реализации по умолчанию.❌ DISALLOWED: изменение значения общедоступной константы или элемента перечисления
❌ НЕ ДОПУСКАЕТСЯ: Изменение типа свойства, поля, параметра или возвращаемого значения
❌ ЗАПРЕЩЕНО: добавление, удаление или изменение порядка параметров
❌ DISALLOWED: добавление или удаление ключевого слова in, out или ref из параметра
✔️ РАЗРЕШЕНО: изменение
refпараметра наref readonlyИзменение параметра из
refнаref readonlyсовместимо с исходными вызовами на существующих сайтах вызовов, которые передают аргументы с модификаторомref— такие вызовы продолжают компилироваться без каких-либо изменений. В отличие от измененияrefнаin, параметрref readonlyне позволяет вызывающим механизмам передавать rvalue (непеременные); компилятор выдает предупреждение, если аргумент не является переменной. Существующиеrefместа вызовов остаются валидными.❌ НЕДОПУСТИМО: Изменение
inпараметра наref readonlyМеста вызова, которые передают
inаргументы безinмодификатора (который компилятор разрешает дляinпараметров), получат предупреждение, когда параметр изменится наref readonly, так какref readonlyтребует, чтобы аргумент передавался по ссылке. Вызывающие абоненты, которые обрабатывают предупреждения как ошибки, будут испытывать критическое изменение источника.❌ ЗАПРЕЩЕНО: переименование параметра (включая изменение его регистра)
Это считается серьезным нарушением по двум причинам:
Он нарушает сценарии с поздними привязками, такие как функция поздней привязки в Visual Basic и dynamic в C#.
Она нарушает совместимость источников , когда разработчики используют именованные аргументы.
❌ ЗАПРЕЩЕНО: изменение возвращаемого
refзначения на возвращаемоеref readonlyзначение❌️ ЗАПРЕЩЕНО: изменение возвращаемого значения с
ref readonlyнаrefв виртуальном методе или интерфейсе❌ ЗАПРЕЩЕНО: добавление или удаление abstract у члена
❌ DISALLOWED: удаление виртуального ключевого слова из элемента класса
❌ ЗАПРЕЩЕНО: добавление ключевого слова virtual в член
Хотя это часто не является критическим изменением, так как компилятор C# обычно выдает инструкции callvirt Intermediate Language (IL) для вызова не-виртуальных методов (инструкция
callvirtвыполняет проверку на null, в то время как обычный вызов такой проверки не выполняет), это поведение не гарантировано по нескольким причинам:- C# — это не единственный язык, который поддерживает .NET.
- Компилятор C# все чаще пытается оптимизировать
callvirt, чтобы привести к обычному вызову, когда целевой метод не является виртуальным и, вероятно, не является null (например, метод, доступ к которому осуществляется через оператор распространения ?. null).
Создание виртуального метода означает, что код потребителя часто вызывает его не в виртуальной среде.
❌ ЗАПРЕЩЕНО: создание абстрактного виртуального члена
Виртуальный член предоставляет реализацию метода, которую можно переопределить производным классом. Абстрактный член не предоставляет реализации и должен быть переопределен.
❌ DISALLOWED: добавление ключевого слова sealed в член интерфейса
Добавление
sealedк члену интерфейса по умолчанию сделает его невиртуальным, что предотвратит вызов реализации этого члена в производном типе.❌ ЗАПРЕЩЕНО: добавление абстрактного элемента в общедоступный тип, имеющий доступные (общедоступные или защищенные) конструкторы и который не является запечатанным
❌ ЗАПРЕЩЕНО: добавление или удаление ключевого слова static из члена
❌ DISALLOWED: добавление перегрузки, которая исключает существующую перегрузку и определяет другое поведение.
Это ломает существующие программы, привязанные к предыдущей перегрузке функций. Например, если класс имеет одну версию метода, который принимает UInt32, существующий клиент успешно обратится к этой перегрузке при передаче значения Int32. Однако при добавлении перегрузки, которая принимает значение Int32при повторной компиляции или использовании последней привязки, компилятор теперь привязывается к новой перегрузке. Если результаты поведения отличаются, это может быть критическое изменение.
❓ ТРЕБУЕТСЯ РЕШЕНИЕ: добавление OverloadResolutionPriorityAttribute в существующую перегрузку или изменение его приоритета
OverloadResolutionPriorityAttribute влияет на разрешение перегрузки на исходном уровне: вызывающие объекты, которые перекомпилируются, могут выбрать другую перегрузку, чем раньше. Предполагаемое использование заключается в добавлении атрибута к новой, лучшей перегрузке, чтобы компилятор предпочитал её по сравнению с существующими. Добавление его в существующую перегрузку или изменение приоритета для уже имеющей атрибут перегрузки может быть изменением, нарушающим совместимость, так как вызывающие объекты, которые перекомпилируются, могут изменить поведение.
✔️ РАЗРЕШЕНО. Добавление
allows ref structанти-ограничения в параметр универсального типаДобавление
allows ref structрасширяет перечень типов, которые можно использовать в качестве аргументов типа, позволяя использоватьref structтипы. Существующие вызовы, использующие аргументы не типаref struct, не затрагиваются. Обобщенный метод или тип должен соответствовать правилам безопасности ссылочных типов для всех экземпляров этого параметра типа.❌ DISALLOWED: удаление
allows ref structанти-ограничения из параметра универсального типаУдаление
allows ref structограничивает типы, которые вызывающие функции могут использовать в качестве аргументов типов. Любой вызывающий, который передаетref structкак аргумент типа, перестанет компилироваться.❌ DISALLOWED: добавление конструктора в класс, который ранее не имел конструктора, без добавления конструктора без параметров
❌️ ЗАПРЕЩЕНО: добавление только для чтения в поле
❌ ЗАПРЕЩЕНО: уменьшение видимости элемента
Это включает уменьшение видимости защищенного элемента при наличии доступных (
publicилиprotected) конструкторов, а тип незапечатан. Если это не так, допускается уменьшение видимости защищенного члена.Допускается повышение видимости члена.
❌ ЗАПРЕЩЕНО: изменение типа элемента
Возвращаемое значение метода или типа свойства или поля нельзя изменить. Например, сигнатура метода, возвращающего объект Object , не может быть изменена для возврата Stringили наоборот.
❌ DISALLOWED: Добавление экземплярного поля в структуру, которая не имеет закрытых полей
Если у структуры есть только открытые поля или нет полей вообще, вызывающие могут объявлять локальные переменные этого типа структуры без вызова конструктора структуры или первой инициализации локальной переменной в
default(T), при условии, что все открытые поля заданы в структуре до первого использования. Добавление новых полей — общедоступных или неопубликованных — в такую структуру — это критическое изменение источника для этих вызывающих пользователей, так как компилятору теперь потребуется инициализировать дополнительные поля.Кроме того, добавление любых новых полей — общедоступных или необщедоступных — в структуру, не содержащую полей или содержащую только общедоступные поля, является изменением, нарушающим двоичную совместимость вызывающих элементов, к которым применен
[SkipLocalsInit]в их коде. Так как компилятор не знал об этих полях во время компиляции, он может выдавать IL, который не полностью инициализирует структуру, что приводит к созданию структуры из неинициализированных данных стека.Если у структуры есть какие-либо непубличные поля, компилятор уже требует инициализацию с помощью конструктора или
default(T), и добавление новых полей экземпляра не является критическим изменением.❌ ЗАПРЕЩЕНО: запуск существующего события, когда он никогда не был запущен раньше
Изменения поведения
Сборки
✔️ РАЗРЕШЕНО: Сделать сборку портативной, если те же платформы по-прежнему поддерживаются
❌ ЗАПРЕЩЕНО. Изменение имени сборки
❌ ЗАПРЕЩЕНО: изменение открытого ключа сборки
Свойства, поля, параметры и возвращаемые значения
✔️ РАЗРЕШЕНО: изменение значения свойства, поля, возвращаемого значения или параметра out на более производный тип
Например, метод, возвращающий тип Object , может возвращать String экземпляр. (Однако сигнатура метода не может измениться.)
✔️ РАЗРЕШЕНО: увеличение диапазона принятых значений для свойства или параметра, если член не является виртуальным.
Хотя диапазон значений, которые могут быть переданы методу или возвращены членом, может расширяться, параметр или тип члена не может изменяться. Например, в то время как значения, передаваемые методу, могут расширяться от 0 до 124 до 0–255, тип параметра не может измениться с ByteInt32.
❌ DISALLOWED: увеличение диапазона принятых значений для свойства или параметра, если член является виртуальным
Это изменение нарушает существующие переопределенные элементы, которые не будут работать правильно для расширенного диапазона значений.
❌ ЗАПРЕЩЕНО: уменьшение диапазона принятых значений для свойства или параметра
❌ Недопустимо: увеличение диапазона возвращаемых значений для свойства, поля, возвращаемого значения или параметра out
❌ DISALLOWED: изменение возвращаемых значений для свойства, поля, возвращаемого метода или параметра out
❌ ЗАПРЕЩЕНО: изменение значения по умолчанию свойства, поля или параметра
Изменение или удаление значения параметра по умолчанию не является двоичным разрывом. Удаление значения по умолчанию параметра — это разрыв источника, и изменение значения по умолчанию параметра может привести к разрыву поведения после перекомпиляции.
По этой причине удаление значений по умолчанию параметров допустимо в конкретном случае перемещения этих значений по умолчанию в новую перегрузку метода, чтобы устранить неоднозначность. Например, рассмотрим существующий метод
MyMethod(int a = 1). Если вы вводите перегрузкуMyMethodс двумя необязательными параметрамиaиb, вы можете сохранить совместимость, переместив значениеaпо умолчанию в новую перегрузку. Теперь две перегрузки – этоMyMethod(int a)иMyMethod(int a = 1, int b = 2). Этот шаблон позволяетMyMethod()компилировать.❌ ЗАПРЕЩЕНО: изменение точности числового возвращаемого значения
❓ ТРЕБУЕТСЯ РЕШЕНИЕ. Изменение синтаксического анализа входных данных и создание новых исключений (даже если поведение синтаксического анализа не указано в документации
❌ DISALLOWED: добавление или удаление типов вариантов из
unionобъявленияДобавление или удаление типа заявления из
unionтипа является как двоичным нарушением, так и нарушением исходного кода.Тесты сопоставления шаблонов перестают быть исчерпывающими после добавления типа случая. Компилятор помечает выражения сопоставления шаблонов как не исчерпывающие. Во время выполнения непредвиденные значения вызывают ошибки во время выполнения. Удаление типа кейса удаляет объявление конструктора для этого типа кейса.
Исключения
✔️ РАЗРЕШЕНО: создание более производного исключения, чем существующее исключение
Так как новое исключение является подклассом существующего исключения, предыдущий код обработки исключений продолжает обрабатывать исключение. Например, в .NET Framework 4 методы создания и извлечения языка и региональных параметров начали вызывать методы CultureNotFoundException вместо ArgumentException, если не удалось найти язык и региональные параметры. Поскольку CultureNotFoundException производный от ArgumentException, это приемлемое изменение.
✔️ РАЗРЕШЕНО: создание более конкретного исключения, чем NotSupportedException, NotImplementedExceptionNullReferenceException
✔️ РАЗРЕШЕНО: создание исключения, которое считается невосстановимым
Неустранимые исключения не должны быть пойманы, но вместо этого должны обрабатываться высокоуровневым обработчиком "catch-all". Таким образом, пользователи не должны иметь код, который перехватывает эти явные исключения. Неустранимые исключения:
✔️ РАЗРЕШЕНО: создание нового исключения в новом пути кода
Исключение должно применяться только к новому пути кода, который выполняется с новыми значениями параметров или состоянием, и не может выполняться существующим кодом, предназначенным для предыдущей версии.
✔️ РАЗРЕШЕНО: удаление исключения для обеспечения более надежного поведения или новых сценариев
Например, метод,
Divideкоторый ранее обрабатывал только положительные значения и выбрасывал исключение ArgumentOutOfRangeException в противном случае, можно изменить так, чтобы он поддерживал как отрицательные, так и положительные значения, не вызывая исключения.✔️ РАЗРЕШЕНО: изменение текста сообщения об ошибке
Разработчики не должны полагаться на текст сообщений об ошибках, которые также изменяются на основе языка и региональных параметров пользователя.
❌ ЗАПРЕЩЕНО: создание исключения в любом другом случае, который не указан выше
❌ ЗАПРЕЩЕНО: удаление исключения в любом другом случае, который не указан выше
Атрибуты
✔️ РАЗРЕШЕНО. Изменение значения атрибута, который не наблюдается
❌ Запрещено: изменение значения атрибута, который является наблюдаемым
❓ ТРЕБУЕТСЯ РЕШЕНИЕ: удаление атрибута
В большинстве случаев удаление атрибута (например NonSerializedAttribute) является критическим изменением.
Поддержка платформы
✔️ РАЗРЕШЕНО: поддержка операции на платформе, которая ранее не поддерживается
❌ ЗАПРЕЩЕНО. Не поддерживается или теперь требуется определенный пакет обновления для операции, которая ранее поддерживалась на платформе.
Внутренние изменения реализации
❓ ТРЕБУЕТ РАССМОТРЕНИЯ: изменение площади поверхности внутренней структуры
Такие изменения обычно допускаются, хотя они нарушают закрытую рефлексию. В некоторых случаях, когда популярные сторонние библиотеки или большое количество разработчиков зависят от внутренних API, такие изменения могут быть запрещены.
❓ ТРЕБУЕТ ОЦЕНКИ: Изменение внутренней реализации члена
Эти изменения обычно допустимы, хотя они нарушают внутренние механизмы анализа. В некоторых случаях, когда код клиента часто зависит от частного отражения или когда изменение вводит непреднамеренные побочные эффекты, эти изменения могут быть запрещены.
✔️ РАЗРЕШЕНО: повышение производительности операции
Возможность изменения производительности операции важна, но такие изменения могут нарушить код, который зависит от текущей скорости операции. Это особенно верно для кода, зависящего от хронологии асинхронных операций. Изменение производительности не должно влиять на другое поведение обсуждаемого API; в противном случае изменение будет критическим.
✔️ РАЗРЕШЕНО: косвенно (и часто отрицательно) изменение производительности операции
Если рассматриваемое изменение не классифицируется как нарушающее совместимость по другой причине, это приемлемо. Часто необходимо предпринять действия, которые могут включать дополнительные операции или добавлять новые функции. Это почти всегда влияет на производительность, но может быть важно, чтобы API в вопросе функционировал должным образом.
❌ ЗАПРЕЩЕНО: изменение синхронного API на асинхронный (и наоборот)
Изменения в коде
✔️ РАЗРЕШЕНО: добавление парамов в параметр
❌ DISALLOWED: изменение структуры данных на класс и наоборот
❌ ЗАПРЕЩЕНО: добавление checked выражения в блок кода
Это изменение может привести к тому, что код, который ранее выполнялся, будет выбрасывать исключение OverflowException, что неприемлемо.
❌ DISALLOWED: удаление параметров из параметра
❌ НЕ ДОПУСКАЕТСЯ: изменение типа параметра
paramsколлекцииНачиная с C# 13,
paramsпараметры поддерживают коллекционные типы, отличные от массива, включая Span<T>, ReadOnlySpan<T>, типы структур или классов, которые реализуют IEnumerable<T> и имеют доступный конструктор без параметров и метод экземпляраAdd, а также определенные типы интерфейсов, такие как IList<T>. Изменение типа коллекции существующегоparamsпараметра (например, сparams T[]наparams ReadOnlySpan<T>) изменяет сигнатуру IL метода и является двоичным критическим изменением. Вызовы, скомпилированные в предыдущей версии, должны быть перекомпилированы.✔️ РАЗРЕШЕНО. Преобразование метода расширения в синтаксис члена блока расширения
Начиная с C# 14, вы можете объявлять члены расширения, используя блоки
extension, в дополнение к более старому синтаксису с параметромthis. Обе формы создают идентичный IL, поэтому вызывающие не могут различать их. Преобразование существующих методов расширения в новый синтаксис блока расширения совместимо с двоичным и исходным кодом.❌ ЗАПРЕЩЕНО: изменение порядка, в котором происходят события
Разработчики могут разумно ожидать, что события будут запускаться в одном порядке, и код разработчика часто зависит от порядка запуска событий.
❌ ЗАПРЕЩЕНО: запрет вызова события для заданного действия
❌ ЗАПРЕЩЕНО: изменение количества вызовов заданных событий
❌ НЕ РАЗРЕШЕНО: Добавлять FlagsAttribute к перечисляемому типу