ПОВЫШЕНИЕ ПРОИЗВОДИТЕЛЬНОСТИ БЕЗОПАСНОГО КОДА:
Индексирование полей fixed без закрепления
Локальные переменные ref могут быть переназначены
Массивы stackalloc поддерживают инициализаторы
Больше типов поддерживают инструкцию fixed
Расширенные универсальные ограничения
УЛУЧШЕНИЕ СУЩЕСТВУЮЩИХ ФУНКЦИЙ:
Поддержка == и != для кортежей
Подключение атрибутов к резервным полям для автоматически реализуемых свойств
Критерии для разрешения перегрузки метода in
Расширение переменных выражений в инициализаторах
Улучшенный отбор потенциальных перегрузок
НОВЫЕ ПАРАМЕТРЫ КОМПИЛЯТОРА:
Параметр компилятора publicsign
Расширяются возможности гарантированно безопасного кода –
приоритетно использовать безопасные конструкции.
Представлена следующая структура с массивом фиксированного размера (Буферы фиксированного размера):
unsafe struct MyBuffer
{
public fixed int fixedBuffer[10];
}
В более ранних версиях C# переменную необходимо закрепить, чтобы
получить доступ к целым числам, входящим в fixedBuffer
:
// оператор fixed устанавливает указатель на первый элемент
unsafe void AccessMyBuffer()
{
fixed (int* intPtr = myBuffer.fixedBuffer)
{
int p = intPtr[5];
}
}
Теперь такой код компилируется в безопасном контексте, не требуется
объявлять второй фиксированный указатель int* intPtr
на fixedBuffer
.
Контекст unsafe
по-прежнему является обязательным.
Переменная p
обращается к одному элементу в fixedBuffer
. Для этого
не нужно объявлять отдельную переменную int*
.
class MyClass
{
MyBuffer myBuffer = default;
unsafe void AccessMyBuffer()
{
int p = myBuffer.fixedBuffer[5];
}
}
До версии C# 7.3 ссылочные локальные переменные (Возвращаемые ссылочные значения) не переназначались
после инициализации так, чтобы они ссылались на другое хранилище.
Теперь локальные переменные ref
можно переназначить другим
экземплярам после инициализации:
// инициализация
ref VeryLargeStruct refLocal = ref veryLargeStruct;
// переназначение, refLocal ссылается на другое хранилище
refLocal = ref anotherVeryLargeStruct;
Синтаксис инициализации массиивов с помощью инициализатора, теперь
применим к массивам, в объявлении которых есть stackalloc
(stackalloc):
var arr = new int[3] {1, 2, 3};
var arr2 = new int[] {1, 2, 3};
int* pArr = stackalloc int[3] {1, 2, 3};
int* pArr2 = stackalloc int[] {1, 2, 3};
Оператор fixed
работает с дополнительными типами, помимо массивов,
строк, буферов фиксированного размера и неуправляемых переменных,
можно применять для System.Span<T>
и связанных типов.
Любой тип, реализующий метод GetPinnableReference
, который
возвращает ref T
или ref readonly T
, можно зафиксировать
(GetPinnableReference
должен преобразовывать переменную ref
в
неуправляемый тип).
Можно указать тип System.Enum
или System.Delegate
в качестве
ограничения базового класса для параметра типа.
Доступно новое ограничение unmanaged
, чтобы указать, что параметр
типа должен быть неуправляемым типом.
Типы кортежей в C# теперь поддерживают операторы ==
и !=
работающие
путём сравнения каждого элемента левого аргумента с каждым
элементом правого аргумента по порядку.
Оператор ==
перестаёт сравнивать элементы, как только будет
обнаружена неравная пара.
Оператор !=
перестаёт сравнивать элементы, как только будет
обнаружена равная пара.
var left = (a: 5, b: 10);
var right = (a: 5, b: 10);
Console.WriteLine(left == right); // true
Если один из кортежей допускает значение NULL
, функция проверки
кортежей на равенство выполняет неявное преобразование:
var left = (a: 5, b: 10);
var right = (a: 5, b: 10);
(int a, int b)? nullableTuple = right;
Console.WriteLine(left == nullableTuple); // true
Дополнительно выполняется неявное преобразование каждого элемента
обоих кортежей (преобразования для использования форм, допускающих
значение NULL
, расширяющие преобразования и другие неявные
преобразования):
var left = (a: 5, b: 10);
(int? a, int? b) nullableMembers = (5, 10);
Console.WriteLine(left == nullableMembers); // true
(long a, long b) longTuple = (5, 10);
Console.WriteLine(left == longTuple); // true
(long a, int b) longFirst = (5, 10);
(int a, long b) longSecond = (5, 10);
Console.WriteLine(longFirst == longSecond); // true
Имена элементов кортежей не участвуют в тестах на равенство. Если один из операндов является литералом кортежа с явными именами, компилятор генерирует предупреждение:
(int a, string b) pair = (1, "Hello");
(int z, string y) another = (1, "Hello");
// имена элементов не участвуют
Console.WriteLine(pair == another); // true
// литерал содержит различные имена участников
Console.WriteLine(pair == (z: 1, y: "Hello")); // warning
Кортежи могут содержать вложенные кортежи. Функция проверки кортежей на равенство сравнивает "форму" каждого операнда по вложенным кортежам:
(int, (int, int)) nestedTuple = (1, (2, 3));
Console.WriteLine(nestedTuple == (1, (2, 3)) ); // true
Целевое значение field
устанавливает атрибут (Атрибуты) резервной переменной
автоматически реализуемого свойства.
Поддерживается следующий синтаксис:
[field: SomeThingAboutFieldAttribute]
public int SomeProperty { get; set; }
Атрибут SomeThingAboutFieldAttribute
применяется к резервному полю,
созданному компилятором для SomeProperty
.
Устранена неоднозначность при добавлении модификатора аргумента in
(C# 7.2):
static void M(S arg);
static void M(in S arg);
Перегрузка по значению (первая в примере) считается лучше, чем перегрузка по атрибуту "только для чтения".
Для вызова версии со ссылочным аргументом "только для чтения",
необходимо при вызове метода указать модификатор in
.
Синтаксис, позволяющий с версии C# 7.0 (C# 7.0) объявлять переменные out
,
поддерживает инициализаторы полей, инициализаторы свойств,
инициализаторы конструктора и предложения запроса.
public class B
{
// конструктор задаёт значение 'j'
public B(int i, out int j)
=> j = i;
}
public class D : B
{
// вызывается базовый конструктор
public D(int i) : base(i, out var j)
{
Console.WriteLine($"значение 'j' равно {j}");
}
}
- Если группа методов содержит элементы экземпляра и статические элементы:
-
компилятор отклоняет все элементы экземпляра при вызове метода без экземпляра-получателя и вне контекста экземпляра.
-
компилятор отклоняет статические элементы, если метод был вызван с экземпляром-получателем.
Если получатель не указан, компилятор включает в статический контекст только статические элементы, а в противном случае – статические элементы и элементы экземпляра.
Если получатель невозможно однозначно определить как экземпляр или тип, компилятор включает и те, и другие элементы.
Статический контекст, в котором неявный this
экземпляр-получатель
нельзя использовать, включая тело членов, для которых this
не
задано, как например статические элементы, а также области, где
this
не может использоваться, такие как инициализаторы полей и
инициализаторы конструкторов.
-
Если группа методов содержит некоторые универсальные методы, у которых аргументы типа не удовлетворяют ограничениям, такие элементы удаляются из набора кандидатов.
-
При преобразовании группы методов из набора удаляются методы-кандидаты, у которых возвращаемый тип не соответствует возвращаемому типу делегата.
Параметр компилятора -publicsign
указывает, что сборку нужно
подписать открытым ключом, фактически не подписывая сборку, сборка
помечается как подписанная (задаёт в сборке бит, который сообщает
среде выполнения, что файл подписан).
Позволяет создавать подписанные сборки из проектов с открытым кодом с помощью открытого ключа.
С параметром -publicsign
необходимо использовать параметр -keyfile
или -keycontainer
(каждый определяет открытый ключ).
Параметры -publicsign
и -delaysign
– взаимоисключающие.
При таком подписывании в сборку добавляется открытый ключ, устанавливается флаг "подписано", хотя фактически сборка не подписывается закрытым ключом. Такой подход называют "фиктивным подписыванием" или "подписыванием OSS", применяемым на проектах с открытым исходным кодом.
Установка параметра в среде разработки Visual Studio:
-
Открыть страницу свойств проекта.
-
Изменить свойство
Delay sign only
.
Параметр компилятора -pathmap
определяет способ сопоставления
физических путей с именами исходных путей, выводимыми компилятором,
управляет исходными путями, которые компилятор записывает в
PDB-файлы или для CallerFilePathAttribute
.
-pathmap:path1=sourcePath1,path2=sourcePath2
-
path1
– полный путь к исходным файлам в текущем окружении. -
sourcePath1
– исходный путь подставляется вместо path1 в любых выходных файлах.
*разделить запятыми для указания нескольких сопоставленных исходных путей
Компилятор записывает исходный путь в выходные данные по следующим причинам:
-
Исходный путь подставляется вместо аргумента, когда
CallerFilePathAttribute
применяется как необязательный параметр. -
Исходный путь внедряется как PDB-файл.
-
Путь к PDB-файлу внедряется в PE-файл (переносимый исполняемый файл).
Этот параметр сопоставляет каждый физический путь на компьютере, где выполняется компилятор, с соответствующим путем, который должен быть записан в выходные файлы.
csc -pathmap:C:\work\tests=\publish t.cs