Эквиваленты кортежей C # 3.0 (для бедных мужчин)
Вопрос
В C # 3.0 я время от времени нахожусь в поисках способов имитировать понятие кортежа. Со временем у меня появились различные «бедняки» реализации, вот несколько из них:
Массив базовых объектов :
object[] poorTuple = new object[]{foo,bar,baz}; // basic object array
Более типизированный , HoHoHo ...
KeyValuePair<TypeA, KeyValuePair<TypeB, TypeC>> poorTuple;
Реализация класса , который может использовать вывод типов (взято из функционального программирования для Реальный мир )
public static class Tuple{
public static Tuple<T1, T2> Create<T1 foo, T2 bar>{
return new Tuple<T1, T2>(foo, bar);
}
}
// later:
var data = Tuple.Create("foo", 42);
Вопросы:
<Ол>Любые другие способы создать кортеж для бедного человека в C # 3.0 (или языке выбора, в котором отсутствует структура данных). Р>
Каков лучший способ получить кортеж в C # 3.0 - если у кого-то есть рекомендации по библиотеке, это приветствуется.
В какой момент (да, обобщите для меня) имеет смысл создавать определенный тип, а не что-то вроде списка или кортежа? (ищет практические правила)
Решение
Вы можете создавать анонимные типы, которые функционируют аналогично кортежам, кроме как с полезными именами:
var itemsWithChildCounts
= myObjects.Select(x => new { Name = x.Name, Count = x.Children.Count() });
Другие советы
Вот код для универсального кортежа, взятый из статья в апрельском выпуске Visual Studio Magazine .
public struct Tuple<T1, T2> : IEquatable<Tuple<T1, T2>>
{
private readonly T1 first;
public T1 First
{
get { return first; }
}
private readonly T2 second;
public T2 Second
{
get { return second; }
}
public Tuple(T1 f, T2 s)
{
first = f;
second = s;
}
#region IEquatable<Tuple<T1,T2>> Members
public bool Equals(Tuple<T1, T2> other)
{
return first.Equals(other.first) &&
second.Equals(other.second);
}
public override bool Equals(object obj)
{
if (obj is Tuple<T1, T2>)
return this.Equals((Tuple<T1, T2>)obj);
else
return false;
}
public override int GetHashCode()
{
return first.GetHashCode() ˆ second.GetHashCode();
}
#endregion
}
Для 1 - 2: я предпочитаю реализовать свой собственный класс кортежей. Реализация, которую вы украли, достойная. Это должно работать хорошо.
Для 3: Вот мое эмпирическое правило - как только вы собираетесь повторно использовать эту функциональность в нескольких методах (с одинаковыми типами, имеющими одинаковое значение), или если вы используете ее в любом публичном API, я думаю, что это время для реализации «реального» класс с вашими конкретными типами.
Так как вы спросили мнение, мое будет состоять в том, чтобы всегда создавать тип - я не могу найти причину, чтобы этого не делать.
Чаще всего вы можете обнаружить, что вам действительно нужен тип (основное назначение - либо сохранить два элемента в коллекции, либо вернуть два элемента из вызова метода - в обоих случаях, если элементы не связаны тесно , вы, вероятно, делаете что-то не так).
Я думаю, что хорошо создавать новый тип, когда имеет смысл ввести новый набор значений в вашу программу. Например, если вы пишете сложный калькулятор, то создайте тип комплексного числа. Если вы действительно хотите «склеить» несколько переменных вместе на мгновение, тогда кортеж, вероятно, будет лучшим выбором. Допустим, у вас есть простая функция, которая собирает два числа с консоли ... вы можете сделать что-то вроде:
static void GetNumbers(out int x, out int y) { ... }
...
int x, y;
GetNumbers(out x, out y);
Я думаю, что обычно имеет смысл, когда " getter " Функция имеет возвращаемое значение, но в этом случае это не так, потому что я не могу иметь два возвращаемых значения. Я мог бы пойти и создать новый тип с именем TwoNumbers и использовать его, но я думаю, что это быстро становится скорее проблемой, чем решением. Если в C # есть кортежи, я могу сделать что-то вроде следующего:
static (int, int) GetNumbers() { ... }
...
int x, y;
(x, y) = GetNumbers();
Теперь действительно интересный вопрос: хотя в C # нет этой функции, могу ли я реализовать ее самостоятельно с помощью библиотеки? Код, который вы предлагаете, является началом, но не позволяет мне назначать, как я сделал во втором примере. Я не уверен насчет C #, но вы можете быть очень близки к этому в C ++, используя тот факт, что вызов функции может быть левым операндом оператора присваивания, когда его возвращаемое значение является ссылочным типом , Примите во внимание следующее:
// this class is implemented much like yours only with C++
template<typename T1, typename T2> class tuple { ... }
...
template<typename T1, typename T2> tuple<T1, T2>& tie(T1 a, T2 b) { ... }
...
template<typename T1, typename T2> tuple<T1, T2> get_numbers() { ... }
...
int x, y;
tie(x, y) = get_numbers();
Я бы сказал, что это чертовски близко ... вместо (x, y) =
у нас есть tie (x, y) =
. Если вас интересуют подробности реализации, я отправлю вас в библиотеку TR1, где я впервые узнал об этом:
http: //www.boost .org / док / ЛИЭС / 1_41_0 / ЛИЭС / кортеж / DOC / tuple_users_guide.html # ярусы
Итак, чтобы вернуть все это в C # 3.0 ... мы, безусловно, можем создать универсальный класс кортежей, как вы показали, но можем ли мы создать функцию типа tie to " unpack " кортеж в C #? Я не пробовал, звучит как весело. Без чего-то подобного я бы не хотел, чтобы библиотека кортежей C # распространялась через мой код. Р>
Мне не нравится вывод типов в C #, так как он слишком часто используется для более короткой записи. Конечно, это очень крутая функция, но люди злоупотребляют ею ИМХО, как в этом случае.
В этом случае я бы указал тип явно, чтобы избежать путаницы с типом (то есть, возможно, 42 - это длинный, или байт, или короткий).
Так почему бы не иметь простой класс кортежей, который может быть реализован всего за несколько строк? И если вы ленивы, вы можете даже написать несколько методов расширения для своего класса кортежей. Это делает жизнь проще, но и чище.
Не вижу смысла в том, чтобы иметь "причудливый" класс кортежа вместо представленного вами универсального класса (за исключением вывода типа). Р>