- •Доступ к элементам массива
- •Многомерные массивы
- •Зубчатые массивы
- •Класс Array
- •Создание массивов
- •Копирование массивов
- •Сортировка
- •Массивы в качестве параметров
- •Ковариантость массивов
- •Перечисления
- •Интерфейс iEnumerator
- •Оператор foreach
- •Оператор yield
- •Различные способы итерации по содержимому коллекций
- •Возврат перечислителей посредством yield return
- •Кортежи
- •Структурное сравнение
- •If (t1.Equals(t2)) Console.WriteLine("одинаковое содержимое");
Кортежи
Массивы комбинируют объекты одного типа, а кортежи (tuple) могут комбинировать объекты различных типов. Понятие кортежей происходит из языков функционального программирования, таких как F#, где они часто используются. С появлением .NET 4 кортежи стали доступны в .NET Framework для всех языков .NET.
В .NET 4 определены восемь обобщенных классов Tuple и один статический класс Tuple, который служит фабрикой кортежей. Существуют различные обобщенные классы Tuple для поддержки различного количества элементов; например, Tuple<T1> содержит один элемент, Tuple<T1,Т2> два элемента и т.д.
Метод Divide() демонстрирует возврат кортежа с двумя членами Tuple<int,int>. Параметры обобщенного класса определяют типы членов, которые в данном случае оба целочисленные. Кортеж создан статическим методом Create() статического класса Tuple. Обобщенные параметры метода Create() определяют тип создаваемого экземпляра кортежа. Вновь созданный кортеж инициализируется переменными result и reminder для возврата результата деления:
public static Tuple<int,int> Divide (int dividend, int divisor)
{
int result = dividend/divisor;
int reminder = dividend%divisor;
return Tuple.Create<int, int>(result, reminder);
}
В следующем коде показан вызов метода Divide(). Элементы кортежа могут быть доступны через свойства Item1 и Item2.
var result = Divide(5,2);
Console.WriteLine("результат деления: (0), остаток: {1}",
result.Item1, result.Item2);
В случае если имеется более восьми элементов, которые нужно включить в кортеж, можно использовать определение класса Tuple с восемью параметрами. Последний параметр называется TRest, в котором должен передаваться сам кортеж. Таким образом, есть возможность создавать кортежи с любым количеством параметров.
Для демонстрации этой функциональности напишем следующий код:
public class Tuple<T1, Т2, Т3, Т4, Т5, Т6, Т7, TRest>
Здесь последний параметр шаблона сам тип кортежа, так что можно создать кортеж с любым числом элементов:
var tuple = Tuple.Create<string,string,string,int,int,int,double,
Tuple<int,int> ("Stephanie", "Alina", "Nagel", 2009, 6, 2, 1.37,
Tuple.Create<int,int>(52, 3490));
Структурное сравнение
Как массивы, так и кортежи реализуют интерфейс IStructuralEquatable и IStructuralComparable. Эти интерфейсы появились в .NET 4 и позволяют сравнивать не только ссылки, но и содержимое. Интерфейс реализован явно, поэтому при его использовании необходимо выполнять приведения массивов и кортежей. IStructuralEquatable служит для определения того, имеют ли два кортежа или массива одинаковое содержимое, a IStructuralComparable применяется для сортировки кортежей и массивов.
В следующем примере, демонстрирующем использование IStructuralEquatable, создан класс Person, который реализует интерфейс IEquatable. Этот интерфейс определяет строго типизированный метод Equals(), в котором сравниваются значения свойств FirstName и LastName:
public class Person: IEquatable<Person>
{
public int Id {get; private set; }
public string FirstName {get; set;}
public string LastName {get; set;}
public override string ToString()
{
return String.Format("{0}, {1} {2}", Id, FirstName, LastName);
}
public override bool Equals(object obj)
{
if(obj == null) throw new ArgumentNullException("obj");
return Equals(obj as Person);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
public bool Equals(Person other)
{
if (other == null) throw new ArgumentNullException("other");
return this.Id==other.Id && this.FirstName==other.FirstName &&
this.LastName == other.LastName;
}
}
Ниже создаются два массива элементов Person. Оба они содержат один и тот же объект Person с переменной по имени janet и два разных объекта Person с одинаковым содержимым. Операция сравнения != возвращает true, потому что на самом деле это два разных массива, на которые ссылаются две переменные по имени persons1 и persons2. Поскольку метод Equals() с одним параметром не переопределяется классом Array, то же самое случается и с операцией == при сравнении ссылок они не совпадают.
var janet = new Person {FirstName = "Janet”, LastName = "Jackson"};
Person [] persons1 = { new Person
{
FirstName = "Michael",
LastName = "Jackson"
},
janet
};
Person[] persons2 = { new Person
{
FirstName = "Michael",
LastName = "Jackson"
},
janet
};
if (persons1 != persons2)
Console.WriteLine("разные ссылки");
Вызывая метод Equals(), определенный в IStructuralEquatable как принимающий первый параметр типа object и второй типа IEqualityComparer, можно определить, как именно должно выполняться сравнение, передавая объект, реализующий IEqualityComparer<T>. Реализация IEqualityComparer по умолчанию предоставляется классом EqualityComparer<T>. В ней производится проверка, реализует ли тип интерфейс IEquatable, и вызывается IEquatable.Equals(). Если тип не реализует IEquatable, то для выполнения сравнения вызывается метод Equals() базового класса Object.
Класс Person реализует IEquatable<Person>, где содержимое объектов сравнивается, и оказывается, что массивы действительно включают одинаковое содержимое:
if ((persons1 as IStructuralEquatable).Equals(persons2,
EqualityComparer<Person>.Default))
{
Console.WriteLine("одинаковое содержимое");
}
Теперь будет показано, как то же самое можно сделать с применением кортежей. Ниже создаются два экземпляра кортежей с одинаковым содержимым. Разумеется, поскольку ссылки t1 и t2 указывают на два разный объекта, операция сравнения != возвращает true:
var t1 = Tuple.Create<int, string>(l, "Stephanie");
var t2 = Tuple.Create<int, string>(l, "Stephanie");
if (t1 != t2) Console.WriteLine("не одинаковое содержимое");
Класс Tuple<> предоставляет два метода Equals(): один, переопределяющий реализацию базового класса Object, с object в качестве параметра, а второй определен интерфейсом IStructuralEqualityComparer, с двумя параметрами object и IEqualityComparer. Как показано, другой кортеж может быть передан в первый метод. Чтобы получить ObjectEqualityComparer<object> для сравнения, этот метод использует EqualityComparer<object>.Default. Таким образом, каждый элемент в кортеже сравнивается за счет вызова метода Object.Equals(). Если для каждого элемента возвращается true, конечным результатом метода Equals() также будет true, что мы и видим здесь с одинаковыми значениями int и string: