CLR вдоль и поперек

Построение Tuple

Мэт Эллис (Matt Ellis)

Эта статья основана на предварительной версии Microsoft XML (веб-службы) 4. Сведения могут быть изменены.

Содержание

Существующие типы кортеж
Совместимость
С помощью кортежей из C#
Тип значения или ссылки?
Кортежи произвольного длина
Реализованные интерфейсы
Структурные равенства, сравнение и эквивалентность
IStructualEquatable и IStructuralComparable
Частичная эквивалентность отношения
Оно того стоит

4.0 Предстоящий выпуск Microsoft XML (веб-службы) появился новый тип называется System.Tuple. System.Tuple — это совокупность heterogeneously типизированных данных фиксированного размера. Как массив кортеж имеет фиксированный размер, которые нельзя изменить после его создания. В отличие от массив каждый элемент в кортеж может быть другой тип и кортеж способен обеспечить строгую типизацию для каждого элемента. Рассмотрим пытается использовать массив для хранения значения строковый и целочисленный вместе, как в следующем примере:

        static void Main(string[] args) {
            object[] t = new object[2];
            t[0] = "Hello";
            t[1] = 4;

            PrintStringAndInt((string) t[0], (int) t[1]);
        }

        static void PrintStringAndInt(string s, int i) {
            Console.WriteLine("{0} {1}", s, i);
        }

В приведенном выше коде является неудобных для чтения и опасных из представления от точки безопасность типа. Нет гарантии, элементов кортежа правильный тип при переходе мы их использования. Что делать, если вместо целого числа, мы поместить строку в второй элемент в нашей кортеж следующим образом:

        static void Main(string[] args) {
            object[] t = new object[2];
            t[0] = "Hello";
            t[1] = "World";

            PrintStringAndInt((string) t[0], (int) t[1]);
        }

        static void PrintStringAndInt(string s, int i) {
            Console.WriteLine("{0} {1}", s, i);
        }

Этот код будет компилироваться, но во время выполнения вызова PrintStringAndInt произойдет сбой с InvalidCastException, когда мы пытались приведение строки "World"целое число. Теперь давайте рассмотрим, как может выглядеть код с типом кортеж:

        static void Main(string[] args) {
            Tuple<string, int> t = new Tuple<string, int>("Hello", 4);
            PrintStringAndInt(t.Item1, t.Item2);
        }

        static void PrintStringAndInt(string s, int i) {
            Console.WriteLine("{0} {1}", s, i);
        }

С типом кортежа мы был возможность удалить операторов приведения из нашего кода предоставление нам лучше ошибок во время компиляции. При создании и использовании элементов кортежа, компилятор гарантирует правильность нашей типов.

Как показано в этом примере, это очевидно, что кортеж является полезным типом в Microsoft XML (веб-службы). Сначала он может показаться очень просто расширение. Однако мы обсудим некоторые интересные проблемы разработки, столкнувшись группы при разработке System.Tuple. Мы также рассмотрю более о мы добавили к Microsoft XML (веб-службы) и почему эти дополнения были сделаны.

Существующие типы кортеж

Уже является примером кортежа, перемещаемые вокруг Microsoft XML (веб-службы), System.Collections.Generic пространства имен: KeyValuePair. Хотя KeyValuePair < TKey, TValue >можно рассматривать как же, как < T1, T2 > кортежей, поскольку они оба являются типы, удерживать две вещи, KeyValuePair кажется отличается от кортежей, поскольку evokes отношение между двумя значениями хранит (и с хорошей причины, поддерживает класс словаря). Кроме того кортежей можно произвольно изменять, тогда как KeyValuePair содержит только две вещи: ключ и значение.

В структуре, многие группы для собственной использовать закрытой версии кортеж, но не совместно их версии групп. Библиотеки базовых классов (BCL) группы рассмотрели все эти использует при разработке их версии кортеж. После другими группами слышать, что группы BCL будет добавляться кортеж в Microsoft XML (веб-службы) 4, строгое стремление к использованию кортежей в своем коде слишком Фломастер другими группами. Управляемые расширения Framework (MEF) группы даже занял взгляд на черновик одной спецификации BCL до реализации версии кортежа в своем коде. Позже они выпуска данной версии как часть предварительного просмотра технологии потребителя, поддерживающий комментарий, он был в временные реализации до кортежей были добавлены средой Microsoft .NET Framework!

Совместимость

Одной из крупнейших набор клиентов внутри Microsoft для кортежа было команд языка сами. Хотя C# и языки vb.NET не делать имеют концепцию кортеж как часть языка ядра, является общих компонентом многих функциональных языках. Когда предназначенные для таких языков для платформы Micrososft.NET разработчиками языка пришлось определения управляемого представления кортежа, что приводит к ненужные дублирования. Был один язык, страдающих проблему F #, ранее определен собственный тип кортежа в FSharp.Core.dll, но будет использовать кортеж, добавленные в Microsoft XML (веб-службы) 4.

В дополнение к Включение удаления повторяющихся типов, имеющих общий тип гарантирует, что его легко вызов функций через границы языка. Рассмотрим, что произойдет, если C# добавлена кортеж тип (а также новый синтаксис для его поддержки) языка, но не использовать одинаковые управляемое представление как F #. Если это было в случае любое время, нужно вызвать метод из сборки F #, кортеж в качестве аргумента, пользователь сможет использовать обычный синтаксис C# для кортеж или передать существующий C# кортеж. Вместо этого можно будет вынужден преобразовать кортеж F # кортеж C# и затем вызвать метод. Это наш надеюсь, обеспечивая общий тип кортежа, мы будет сделать взаимодействие управляемых языков кортежей, гораздо проще.

С помощью кортежей из C#

Хотя в некоторых языках вроде F # имеют особый синтаксис для кортежей, можно использовать новый общий тип кортежа из любого языка. Повторное посещение в первом примере можно заметить, что хотя полезным, кортежей может быть чрезмерно подробного языках без синтаксис кортеж:

    class Program {
        static void Main(string[] args) {
            Tuple<string, int> t = new Tuple<string, int>("Hello", 4);
            PrintStringAndInt(t.Item1, t.Item2);
        }

        static void PrintStringAndInt(string s, int i) {
            Console.WriteLine("{0} {1}", s, i);
        }
    }

С помощью ключевого слова var с C# 3.0 мы можно удалить подпись типа на кортеж переменной, которая позволяет немного более удобочитаемым код.

            var t = new Tuple<string, int>("Hello", 4);

Мы также добавили некоторые фабричные методы статические классу кортежа, который облегчает построение кортежи на языке, поддерживающий типов вывод, вроде C#.

            var t = Tuple.Create("Hello", 4);

Не верьте заметно отсутствие типов здесь. Тип t является по-прежнему кортеж < строки, int >и компилятор не позволяет считать элементов различных типов.

Теперь, когда мы рассмотрели, как работает тип кортежа, мы можно взглянуть на структуру позади сам тип.

Тип значения или ссылки?

На первый взгляд нет много типу кортежа и кажется нечто, что может быть разработан и реализованы через длинные выходные. Однако выглядит часто являются deceiving и произошли некоторые очень интересные разработки решения нужных внесенных во время его разработки.

Первый основных критериев была ли рассматривать кортежи либо в качестве типа значения или ссылки. Поскольку они являются постоянными любой момент использовать для изменения значений кортеж, необходимо создать новую. Если они являются ссылочными типами, это означает, может существовать множество мусора, если при изменении элементов кортежа в непрерывном цикле. F # кортежей были ссылочные типы, но произошла чувство из группы, они могут понимают улучшение производительности при двух и трех возможно кортежей элемент типы значений вместо. Некоторые группы создан внутреннего кортежей значение вместо использовать ссылочные типы, так как их сценарии были очень важных создание большого количества управляемых объектов. Они найдены, используя тип значения присваивает их производительность. В наш первый черновик спецификации кортежа мы хранится двух-трех и четыре элемента кортежи как типы значений с остальными, ссылочные типы. Однако во время разработки собрания, включаются представители из других языков было принято решение, это "Разделить"разработки может быть запутанными, из-за Чтобы немного другая семантика между двумя типами. Согласованности в поведение и разработки было определено как более высокий приоритет, чем потенциальных увеличивает производительность. Основываясь на этот ввод, мы изменено разработки так, что все кортежи являются ссылочными типами, хотя мы задаваемые команде F # провести некоторые исследования производительности для просмотра, если она возникла ускорение при использовании типа значения для некоторых размеров кортежей. Он имел хороший способ проверить это, компилятор, написанные на языке F # момента хороший пример больших программ, используемых кортежей в различных сценариях. В конце концов группа F # обнаружила, он был не получить улучшение производительности при некоторых кортежей были типы значений вместо ссылочные типы. Это сделано нам кажется лучшим о нашей решение использовать ссылочные типы для кортежа.

Кортежи произвольного длина

Теоретически кортеж может содержать столько элементов, при необходимости, но мы смогли создать ограниченное число классы для представления кортежей. Возникает два основных вопроса: сколько универсальных параметров должен иметь наибольшее кортежа и как следует увеличить кортежей кодироваться? Выбор количества универсальных параметров, в наибольшей кортеж закончили вверх, отчасти произвольный. Поскольку Microsoft .NET Framework 4 представлены новые версии действие и Func делегатов, которые принимают до восьми универсальных параметров, мы выбрали имеют System.Tuple работают одинаково. Один хорошо особенностью этого макета является есть что соответствие между этими двумя типами. Мы могли бы добавьте метод применить к каждому кортежу принимает соответственно размеру действие или Func одного и того же типа и передать каждый элемент из кортеж в качестве аргумента к делегату. Хотя мы в конечном счете не добавить это для сохранения новой контактной типа, это то, что может быть добавлен в более поздней версии или с помощью методов расширения. Определенно есть удобная симметрии число универсальных параметров, которые принадлежат кортежей, действие и Func. Сторону: По окончании System.Tuple был создан, владельцев действие и создать дополнительные экземпляры каждого типа Funcони пошло до 16 универсальных параметров. В конечном счете мы решили не выполните масти: решение для изменения размера до начала с было отчасти произвольный и мы не чувствовать времени, мы бы тратить Добавление восемь несколько типов кортеж будет стоит.

После того как мы ответили наш первый вопрос, пришлось по-прежнему понять способ представления кортежей с более восьми элементов. Мы решили, что последний элемент кортежа восемь элемент будет вызван "Остальная"и мы потребует ее кортеж. Элементы этот кортеж бы быть восьмой, ninth и т.д, элементов кортежа в целом. Таким образом пользователи хотели восемь элемент кортежа бы создания двух экземпляров классов кортеж. Первым будет кортеж восемь элемент, который будет содержать первые семь элементов кортежа, и второй бы элемент один кортеж, проведения последней кортеж. В C# его может выглядеть следующим образом:

            Tuple.Create(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8));

В языках вроде F #, которые уже имеют конкретные синтаксис кортежей компилятор обрабатывает данная кодировка для вас.

Для языков, предоставляют синтаксис для взаимодействия с кортежей имена свойств, для доступа к каждому элементу не очень интересно, так как они экранированного от разработчика. Это очень важно вопрос языки вроде C#, где необходимо взаимодействовать с типом непосредственно. Мы начали с идее использования первый Item2, и т. д, как имена свойств для элементов, но мы получили некоторые интересные отзывы от рецензентов разработки нашей платформы. Они сказали, что хотя эти имена свойств смысл кто-то думает о них на первый взгляд, предоставляет чувство, тип автоматически сгенерированный, вместо предназначен. Они предлагаемые, мы имя свойства первый, второй и т. д., которых кажутся более доступными. В конечном счете мы отклонены этой обратной связи по по нескольким причинам: Во-первых мы понравилась опыт возможность изменить элемент, доступ к осуществляется изменение один знак в имени свойства;Во-вторых, мы чувствовали английские имена были сложно использовать (четвертый ввода или шестой является больше работы, чем Item4 или Item6) и бы привести к весьма странно возможности IntelliSense, так как имена свойств бы alphabetized вместо отображения по порядку.

Реализованные интерфейсы

System.Tuple реализует очень немного интерфейсов и не универсальных интерфейсов. Команде F # был очень беспокоит кортеж очень простой тип, так как он использует так много различных экземпляров универсального типа продукта. Если кортеж реализации большого числа универсальные интерфейсы, они Фломастер бы влияние на их рабочего набора и размер образа NGen. В свете из этого аргумента нам не удалось найти убедительных причин для кортежа реализовать интерфейсы как IEquatable < T >и IComparable < T >, хотя он переопределяет равно и реализует интерфейс IComparable.

Структурные равенства, сравнение и эквивалентность

Наиболее интересные запрос, мы столкнулись при разработке кортеж было понять, как для поддержки следующих:

  • структурные равенства
  • Сравнение структуры
  • Частичная эквивалентности отношений

Мы сделали столько разработки временного эти понятия как мы делали для кортежа сам. Структурные равенства и сравнения относятся к какой означает Equals для типа как кортеж, содержащий просто других данных.

В F # равенство кортежей и массивы является структурным. Это означает, что двумя массивами или кортежей равны, если их элементы совпадают. Это отличается от C#. По умолчанию содержимое массивов и кортежи, не имеет значения равенство. Он — это расположение в памяти, важно.

Поскольку эта идея структурной равенства и сравнения уже является частью спецификации F #, команде F # уже придумать частичное решение этой проблемы. Однако он применяются только к типам, создаются группы. Поскольку также требуется структурных равенство через массивов, компилятор создается специальный код для тестирования выполнении сравнения равенства в массив. Если это так, его сделать структурных сравнение вместо просто вызова метода Equals для массива. Группа проектирования, выполняемого на несколько различных макетов по устранению такого рода структурных проблемы равенства и сравнения. Он остановился на создание несколько интерфейсов, структурные типы, необходимые для реализации.

IStructualEquatable и IStructuralComparable

IStructualEquatable и IStructuralComparable интерфейсы предоставляют способ для типа согласие на использование центра структурных равенства или сравнения. Они также предоставляют способ сравнения для каждого элемента в объект. С помощью этих интерфейсов, а также специально определенный компаратор, мы может иметь глубокой равенство кортежей и массивы она необходима, когда у нас нет принудительного семантику на всех пользователей типа. Макет обманчиво прост:

    public interface IStructuralComparable {
        Int32 CompareTo(Object other, IComparer comparer);
    }

    public interface IStructuralEquatable {
        Boolean Equals(Object other, IEqualityComparer comparer);
        Int32 GetHashCode(IEqualityComparer comparer);
    }

Эти интерфейсы работать, разделяя процесс итерации элементов структуры типа, для которого реализации интерфейса является ответственным из фактическое сравнение, для которого вызывающий объект отвечает. Компараторов можно выбирать при обеспечивается глубоко структурных равенства (рекурсивно, вызов методов IStructualEquatable или IStructualComparable, если их реализации элементов), неполная один (выполняя не так), либо совершенно. Кортеж и массива теперь реализовать оба эти интерфейсы явным образом.

Частичная эквивалентность отношения

Кортеж также требовалось поддерживают семантику частичной отношений эквивалентности. Примером частичной эквивалентности отношения в Microsoft XML (веб-службы) связь между NaN и других чисел с плавающей запятой. Например: NaN < NaN имеет значение false, однако же содержит для NaN > NaN и NaN == NaN и NaN! = NaN. Это происходит из-за NaN, принципиально incomparable, поскольку он не представляет любое число. Хотя мы возможность кодирования такого рода отношений с операторами, же не хранит для метода CompareTo на IComparable. Это обусловлено CompareTo может возвращать значение, сигналы два значения являются incomparable отсутствует.

F # требует, структурные равенство на кортежи также работает с частичной эквивалентности связи. Поэтому в F # [NaN, NaN] == [NaN, NaN] является false, но так [NaN, NaN]! = [NaN, NaN].

Наш первый под вопросом решение было перегруженные операторы в кортеж. Это работало с помощью операторов для базовых типов, при котором они существовали и добравшись до равно или CompareTo, если они не. Второй параметр было создать новый интерфейс как интерфейс IComparable, но с другой тип возвращаемого, смог передать сигнал где вещи не Сравним случаи. В конечном счете, мы решили, что мы бы хранения на построении нечто подобное, пока мы видели другие примеры частичной эквивалентности необходимые через Microsoft XML (веб-службы). Вместо этого рекомендуется что F # реализации такого рода логику в IComparer и IEqualityComparer методы, которые они передано IStructrual разновидности CompareTo и равно методы обнаружения этих случаях и иметь некий механизм передачи сигналов ожидания диапазона при обнаружении значения NaN.

Оно того стоит

Хотя выполнялся гораздо большую разработки итерации, чем любой ожидается мы смогли создать тип кортежа, который мы кажется достаточно гибок для использования в различных языках, независимо от синтаксической поддержка кортежей. В то же время мы создали интерфейсов, помогающие описать важных понятий структурных равенства и сравнения, которые имеют значение в Microsoft XML (веб-службы) вне кортеж сам.

За последние несколько месяцев команде F # обновил его компилятору использовать System.Tuple как базовый тип для всех F # кортежей. Это гарантирует, что мы может начать построение сторону распространенный тип кортежа через экосистемы Microsoft .NET. В дополнение к интересные разработки кортеж был demoed на конференции разработчиков Professional за этот год как новое средство для Microsoft .NET Framework 4 и полученных зрители гораздо applause. Просмотре видео и просмотр как увлекаюсь разработчики, чтобы начать использование кортежей сделал все время затраченное на это обманчиво простая средство все более стоит.

Вопросы и комментарии направляйте по адресу clrinout@microsoft.com.

Мэтт Ellis работает инженером программного обеспечения в группе библиотеках базовых классов, ответственный за диагностики, изолированное хранилище и другие возможности немного как кортежей. Когда он не думать о платформ или конструкции языка программирования, он проводит свое время с женой Victoria и их двух собак Nibbler и Snoopy.