#c#
#c#
Вопрос:
Я использую .NET 4.8 и объявляю запись с помощью деконструктора:
public record Product
{
public string Name { get; }
public int CategoryId { get; }
public Product(string name, int categoryId)
=> (Name, CategoryId) = (name, categoryId);
public void Deconstruct(out string name, out int categoryId)
=> (name, categoryId) = (Name, CategoryId);
}
Затем я использую следующий код, который компилируется, и он работает нормально :
var product = new Product("VideoGame", 1);
var (name, categoryId) = product;
string s = name;
int i = categoryId;
Пока этот код не работает, генерируется ошибка:
«Ошибка CS0029 Не удается неявно преобразовать тип ‘ConsoleApp4.Product’ в ‘System.Кортеж <строка, int> ‘»):
var product = new Product("VideoGame", 1);
Tuple<string, int> t = product;
string s = t.Item1;
int i = t.Item2;
Объявление var (name, categoryId)
неясно. Что это такое?
Каков тип этой переменной? Как эта конструкция называется в спецификации?
Является ли это автоматически сгенерированным типом за сценой name
и categoryId
его свойствами?
Комментарии:
1. Это называется деконструкцией . К вашему сведению, в a
record
вам не нужно писатьDeconstruct
метод, поскольку он сгенерирован для вас, если только вам не нужно его настраивать. Вашrecord
может быть переписан какpublic record Product(string Name, int CategoryId);
, и это будет означать то же самое2.
var
может автоматически определять тип. Однако оно не указывает на какой-либо конкретный тип3. Если вам не нравится
var (name, categoryId) = product;
, то вы также можете написать(string name, int categoryId) = product;
здесь, что вы видите непосредственно тип ваших переменных.4. Я думал, что
record
типы являются новой функцией c # 9.0 и что c # 9.0 поддерживается только в .net 5 . Я что-то упускаю? Как вы получаете это для компиляции с использованием .NET 4.8?5. @Igor: Я тоже удивлен. Я дважды проверил целевую структуру проекта, и она равна 4.8, но компилятор не знает, что такое «инициализация» в то же время. Я ранее пытался установить .NET 5 на свой компьютер, он завершил установку, но .NET 5 недоступен в настройках проекта.
Ответ №1:
Обратите внимание, что синтаксис
var (name, categoryId) = product;
это деконструкция — это НЕ присвоение кортежу.
Начиная с C # 7.0, вы можете извлекать несколько элементов из кортежа или извлекать несколько полей, свойств и вычисляемых значений из объекта за одну операцию деконструкции. Когда вы деконструируете кортеж, вы присваиваете его элементы отдельным переменным. Когда вы деконструируете объект, вы присваиваете выбранные значения отдельным переменным.
Игнорируя Deconstruct
на мгновение, любой кортеж может быть разбит на отдельные переменные, при условии, что для размещения кортежа будет предоставлено достаточно переменных (или отбрасывания, _
).
например
(string name, int categoryId) = ("Hello", 123);
Присваивает «Hello» name
, а 123 — categoryId
Все приведенные ниже эквивалентны
(string name, int categoryId) = ("Hello", 123); // Types of tuple match position vars
(var name, var categoryId) = ("Hello", 123); // Type variable types are inferred
var (name, categoryId) = ("Hello", 123);
Аналогичным образом, предоставляя подходящие Deconstruct
методы перегрузки или расширения для ваших собственных классов / записей, вы можете назначить несколько переменных out
параметрам соответствующего Deconstruct
метода:
var (name, categoryId) = Product;
который сообщает компилятору искать подходящую Deconstruct
перегрузку для Product
.
В этом случае, поскольку вы используете var
вывод типа для всех деконструированных, деконструктор должен иметь 2 параметра (любого типа, который будет выведен).
Здесь происходят некоторые другие нюансы.
Во-первых, как вы видели, вы можете объявить много разных деконструкций для своей Product
записи, если подписи деконструкций различаются.
Синтаксис кортежа (значения)
public void Deconstruct(out string name, out int categoryId)
=> (name, categoryId) = (Name, CategoryId);
это просто удобная короткая рука для
public void Deconstruct(out string name, out int categoryId)
{
name = Name;
categoryId = CategoryId;
}
Когда вы выполняете следующее назначение:
var (name, categoryId) = product;
-
В этом случае
Product
, поскольку вы используетеvar
вывод типа, деконструктор должен иметь 2 параметра (но любого типа). -
Затем переменные out присваиваются вашим переменным deconstruct, которые вы также назвали
string name
andint categoryId
.
Хотя вы не можете деконструировать непосредственно В System.ValueTuple
or System.Tuple
, вы можете деконструировать ИЗ обоих
var (name, categoryId) = Tuple.Create("Hello", 123); // Old Heap tuples
var (name, categoryId) = ("Hello", 123); // Newer value tuples
Одно из основных применений деконструкции — для краткой записи вручную во время сопоставления с образцом, где вы можете быстро обдумать тип и свойства:
например, вместо
var result = product switch
{
Product x when x.CategoryId == 3 => "You've got a category 3 product",
Product x when string.IsNullOrWhiteSpace(x.Name) => "That product looks broken",
_ => "Just a standard product"
};
Вместо этого вы можете деконструировать и / или отбрасывать по мере необходимости:
var result2 = product switch
{
var (_, cat) when cat == 3 => "You've got a category 3 product",
var (str, _) when string.IsNullOrWhiteSpace(str) => "That product looks broken",
_ => "Just a standard product"
};
Ответ №2:
Я считаю, что это называется именно так deconstruction
(или unpackage
как в python). В общем, это всего лишь функция, появившаяся в C # 7.0, чтобы сэкономить ваше время, не объявляя весь кортеж, а затем получая доступ к его элементам один за другим. Для получения дополнительной информации посмотрите здесь .