Looking for c# Keywords? Try Ask4Keywords

C# Language Языковая поддержка для кортежей


пример

основы

Кортеж представляет собой упорядоченный конечный список элементов. Кортежи обычно используются в программировании как средство совместной работы с одним отдельным объектом вместо индивидуальной работы с каждым из элементов кортежа и для представления отдельных строк (т. Е. «Записей») в реляционной базе данных.

В C # 7.0 методы могут иметь несколько возвращаемых значений. За кулисами компилятор будет использовать новую структуру ValueTuple .

public (int sum, int count) GetTallies() 
{
    return (1, 2);
}

Замечание : для этого в Visual Studio 2017 вам необходимо получить пакет System.ValueTuple .

Если результат метода возврата кортежа присваивается одной переменной, вы можете получить доступ к членам по их определенным именам в сигнатуре метода:

var result = GetTallies();
// > result.sum
// 1
// > result.count
// 2

Деконструкция кортежа

Деконструкция кортежа разделяет кортеж на его части.

Например, GetTallies и присвоение возвращаемого значения двум отдельным переменным деконструирует кортеж для этих двух переменных:

(int tallyOne, int tallyTwo) = GetTallies();

var также работает:

(var s, var c) = GetTallies();

Вы также можете использовать более короткий синтаксис, с var вне () :

var (s, c) = GetTallies();

Вы также можете деконструировать существующие переменные:

int s, c;
(s, c) = GetTallies();

Обмен теперь намного проще (без переменной temp):

(b, a) = (a, b);

Интересно, что любой объект может быть деконструирован путем определения метода Deconstruct в классе:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public void Deconstruct(out string firstName, out string lastName)
    {
        firstName = FirstName;
        lastName = LastName;
    }
}

var person = new Person { FirstName = "John", LastName = "Smith" };
var (localFirstName, localLastName) = person;

В этом случае синтаксис (localFirstName, localLastName) = person вызывает (localFirstName, localLastName) = person Deconstruct на person .

Деконструкцию можно даже определить в методе расширения. Это эквивалентно приведенному выше:

public static class PersonExtensions
{
    public static void Deconstruct(this Person person, out string firstName, out string lastName)
    {
        firstName = person.FirstName;
        lastName = person.LastName;
    }
}

var (localFirstName, localLastName) = person;

Альтернативным подходом для класса Person является определение самого Name как Tuple . Рассмотрим следующее:

class Person
{
    public (string First, string Last) Name { get; }

    public Person((string FirstName, string LastName) name)
    {
        Name = name;
    }
}

Тогда вы можете создать экземпляр человека таким образом (где мы можем взять кортеж в качестве аргумента):

var person = new Person(("Jane", "Smith"));

var firstName = person.Name.First; // "Jane"
var lastName = person.Name.Last;   // "Smith"

Инициализация кортежа

Вы также можете произвольно создавать кортежи в коде:

var name = ("John", "Smith");
Console.WriteLine(name.Item1);
// Outputs John

Console.WriteLine(name.Item2);
// Outputs Smith

При создании кортежа вы можете назначать имена объявлений ad-hoc членам кортежа:

var name = (first: "John", middle: "Q", last: "Smith");
Console.WriteLine(name.first);
// Outputs John

Вывод типа

Множественные кортежи, определенные с одной и той же сигнатурой (совпадающие типы и количество), будут выведены как соответствующие типы. Например:

public (int sum, double average) Measure(List<int> items)
{
    var stats = (sum: 0, average: 0d);
    stats.sum = items.Sum();
    stats.average = items.Average();
    return stats;
}

stats может быть возвращена, так как объявление переменной stats и ответная подпись метода совпадают.

Имена полей отражения и кортежа

Имена членов не существуют во время выполнения. Отражение будет рассматривать кортежи с одинаковым числом и типами членов одинаково, даже если имена участников не совпадают. Преобразование кортежа в object а затем в кортеж с теми же типами членов, но с разными именами, также не вызывает исключения.

Хотя сам класс ValueTuple не сохраняет информацию для имен членов, информация доступна через отражение в TupleElementNamesAttribute. Этот атрибут не применяется к самому кортежу, а к параметрам метода, возвращаемым значениям, свойствам и полям. Это позволяет сохранять имена элементов кортежа в сборках, т. Е. Если метод возвращает (имя строки, int count), имя и количество имен будут доступны вызывающим лицам метода в другой сборке, потому что возвращаемое значение будет отмечено с помощью TupleElementNameAttribute, содержащим значения «имя» и «счет».

Использование с дженериками и async

Новые функции кортежа (с использованием базового типа ValueTuple ) полностью поддерживают дженерики и могут использоваться как общий тип параметра. Это позволяет использовать их с шаблоном async / await :

public async Task<(string value, int count)> GetValueAsync()
{
    string fooBar = await _stackoverflow.GetStringAsync();
    int num = await _stackoverflow.GetIntAsync();

    return (fooBar, num);
}

Использование с коллекциями

Может оказаться полезным иметь набор кортежей в (например) сценарий, в котором вы пытаетесь найти соответствующий кортеж на основе условий, чтобы избежать разветвления кода.

Пример:

private readonly List<Tuple<string, string, string>> labels = new List<Tuple<string, string, string>>()
{
    new Tuple<string, string, string>("test1", "test2", "Value"),
    new Tuple<string, string, string>("test1", "test1", "Value2"),
    new Tuple<string, string, string>("test2", "test2", "Value3"),
};

public string FindMatchingValue(string firstElement, string secondElement)
{
    var result = labels
        .Where(w => w.Item1 == firstElement && w.Item2 == secondElement)
        .FirstOrDefault();

    if (result == null)
        throw new ArgumentException("combo not found");

    return result.Item3;
}

С новыми кортежами могут стать:

private readonly List<(string firstThingy, string secondThingyLabel, string foundValue)> labels = new List<(string firstThingy, string secondThingyLabel, string foundValue)>()
{
    ("test1", "test2", "Value"),
    ("test1", "test1", "Value2"),
    ("test2", "test2", "Value3"),
}

public string FindMatchingValue(string firstElement, string secondElement)
{
    var result = labels
        .Where(w => w.firstThingy == firstElement && w.secondThingyLabel == secondElement)
        .FirstOrDefault();

    if (result == null)
        throw new ArgumentException("combo not found");

    return result.foundValue;
}

Хотя именование на примере кортежа выше является довольно общим, идея соответствующих ярлыков позволяет глубже понять, что делается в коде, ссылаясь на «item1», «item2» и «item3».

Различия между ValueTuple и Tuple

Основной причиной внедрения ValueTuple является производительность.

Название типа ValueTuple Tuple
Класс или структура struct class
Мутируемость (изменение значений после создания) изменчивый неизменный
Имена членов и поддержка других языков да нет ( TBD )

Рекомендации