Эквиваленты кортежей C # 3.0 (для бедных мужчин) - PullRequest
7 голосов
/ 01 декабря 2009

В 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);

Вопросы:

  1. Любые другие способы создать кортеж для бедного человека в C # 3.0 (или языке выбора, в котором отсутствует структура данных).

  2. Каков лучший способ получить кортеж в C # 3.0 - если у кого-то есть рекомендации по библиотеке, это приветствуется.

  3. В какой момент (да, обобщите для меня) имеет смысл создавать определенный тип, а не что-то вроде списка или кортежа? (ищет эмпирические правила)

Ответы [ 7 ]

8 голосов
/ 01 декабря 2009

Вы можете создавать анонимные типы, которые функционируют аналогично кортежам, кроме как с полезными именами:

var itemsWithChildCounts 
    = myObjects.Select(x => new { Name = x.Name, Count = x.Children.Count() });
5 голосов
/ 01 декабря 2009

Вот код универсального кортежа, взятый из статьи Билла Вагнера в апрельском выпуске журнала Visual Studio .

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 голос
/ 01 декабря 2009

Для 1 - 2: я предпочитаю реализовать свой собственный класс кортежей. Реализация, которую вы украли, достойная. Должно работать хорошо.

Для 3: Вот мое эмпирическое правило - как только вы собираетесь повторно использовать эту функциональность в нескольких методах (с одинаковыми типами, имеющими одинаковое значение), или если вы используете его в любом публичном API, я думаю, что это время реализовать «настоящий» класс с вашими конкретными типами.

0 голосов
/ 16 декабря 2009

Я думаю, что хорошо создавать новый тип, когда имеет смысл ввести новый набор значений в вашу программу. Например, если вы пишете сложный калькулятор, то создайте тип комплексного числа. Если вы действительно просто хотите на мгновение «склеить» несколько переменных, тогда кортеж, вероятно, будет лучшим выбором. Допустим, у вас есть простая функция, которая собирает два числа с консоли ... вы можете сделать что-то вроде:

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/doc/libs/1_41_0/libs/tuple/doc/tuple_users_guide.html#tiers

Итак, чтобы вернуть все это обратно в C # 3.0 ... мы, конечно, можем создать общий класс кортежей, как вы показали, но можем ли мы создать функцию типа tie, чтобы "распаковать" кортеж в C #? Я не пробовал, звучит как весело. Без чего-то подобного я бы не хотел, чтобы библиотека кортежей C # распространялась через мой код.

0 голосов
/ 01 декабря 2009

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

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

0 голосов
/ 01 декабря 2009
  1. Метод «Реализация класса» так же хорош, как и собирается (и так или иначе должен быть в следующей версии .NET)
  2. Понятия не имею, я пока держу их в своей личной библиотеке классов.
  3. Я бы подумал о том, чтобы создать для них подходящий класс, как только они станут доступны публике (т.е. когда они больше не будут использоваться только для внутреннего хранения связанных значений для класса).
0 голосов
/ 01 декабря 2009

Мне не нравится вывод типа в C #, так как он слишком сильно используется для более короткой записи. Конечно, это очень крутая функция, но люди злоупотребляют ею ИМХО, как в этом случае.

В этом случае я бы явно указал тип, чтобы избежать путаницы в отношении типа (т. Е., Возможно, 42 - это длинный, или байт, или короткий).

Так почему бы не иметь простой класс кортежей, который может быть реализован всего за несколько строк? И если вы ленивы, вы можете даже написать несколько методов расширения для своего класса кортежей. Это делает жизнь проще, но и чище.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...