Что такое объявление переменной «var (name, CategoryID) = object», вызываемое в C #?

#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;
  
  1. В этом случае Product , поскольку вы используете var вывод типа, деконструктор должен иметь 2 параметра (но любого типа).

  2. Затем переменные out присваиваются вашим переменным deconstruct, которые вы также назвали string name and int 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, чтобы сэкономить ваше время, не объявляя весь кортеж, а затем получая доступ к его элементам один за другим. Для получения дополнительной информации посмотрите здесь .