Есть ли трюк в создании общего списка анонимного типа?

Иногда мне нужно использовать Tuple, например, у меня есть список танков и их целевых танков (они преследуют их или что-то в этом роде):

List<Tuple<Tank,Tank>> mylist = new List<Tuple<Tank,Tank>>();

а затем, когда я перебираю список, я обращаюсь к ним через

mylist[i].item1 ...
mylist[i].item2 ...

Это очень запутанно, и я всегда забываю, что такое item1 и что item2, если бы я мог получить к ним доступ:

mylist[i].AttackingTank...
mylist[i].TargetTank...

Было бы намного понятнее, есть ли способ сделать это, не определяя класс:

MyTuple
{
public Tank AttackingTank;
public Tank TargetTank;
}

Я хочу избежать определения этого класса, потому что тогда мне придется определять много разных классов в разных сценариях, могу ли я сделать "трюк" и сделать это анонимным.

Что-то вроде:

var k = new {Name = "me", phone = 123};
mylist.Add(k);

Проблема, конечно, что у меня нет типа для перехода к списку, когда я его определяю

Ответ 1

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

var list = Enumerable.Empty<object>()
             .Select(r => new {A = 0, B = 0}) // prototype of anonymous type
             .ToList();

list.Add(new { A = 4, B = 5 }); // adding actual values

Console.Write(list[0].A);

Ответ 2

Вы можете использовать List<dynamic>.

 var myList = new List<dynamic>();
 myList.Add(new {Tank = new Tank(), AttackingTank = new Tank()});

 Console.WriteLine("AttackingTank: {0}", myList[0].AttackingTank);

Ответ 3

Вот хак:

var myList = Enumerable.Empty<int>()
    .Select(dummy => new { AttackingTank = default(Tank), TargetTank = default(Tank), })
    .ToList();

Если Tank - тип класса, вы можете написать (Tank)null вместо default(Tank). Вы также можете использовать экземпляр Tank, который у вас есть.


EDIT:

Или:

var myList = Enumerable.Repeat(
    new { AttackingTank = default(Tank), TargetTank = default(Tank), },
    0).ToList();

Если вы создадите общий метод, вам не придется использовать Enumerable.Empty. Это может выглядеть так:

static List<TAnon> GetEmptyListWithAnonType<TAnon>(TAnon dummyParameter)
{
    return new List<TAnon>();
}

Он должен быть вызван с TAnon, который выведен из использования, конечно, как в:

var myList = GetEmptyListWithAnonType(new { AttackingTank = default(Tank), TargetTank = default(Tank), });

Ответ 4

У вас может быть только общий метод, который принимает params и возвращает его:

public static IList<T> ToAnonymousList<T>(params T[] items)
{
  return items;
}

Итак, теперь вы можете сказать:

var list=ToAnonymousList
(
  new{A=1, B=2},
  new{A=2, B=2}
);

Ответ 5

Стоит отметить, что, наконец, есть возможность использовать такой синтаксис. Он был введен в С# 7.0

var tanks = new List<(Tank AttackingTank, Tank TargetTank)>();

(Tank, Tank) tTank = (new Tank(), new Tank());
tanks.Add(tTank);

var a = tanks[0].AttackingTank;
var b = tanks[0].TargetTank;

Ответ 6

Как насчет ExpandoObject?

dynamic tuple = new ExpandoObject(); 
tuple.WhatEverYouWantTypeOfTank = new Tank(); // Value of any type

редактирует:

dynamic tuple = new ExpandoObject();
tuple.AttackingTank = new Tank();
tuple.TargetTank = new Tank();

var mylist = new List<dynamic> { tuple };

//access to items
Console.WriteLine(mylist[0].AttackingTank);

Ответ 7

Или даже более простой

        var tupleList = (
           new[] {
                new { fruit = "Tomato", colour = "red"},
                new { fruit = "Tomato", colour = "yellow"},
                new { fruit = "Apple", colour = "red"},
                new { fruit = "Apple", colour = "green"},
                new { fruit = "Medlar", colour = "russet"}
            }).ToList();

Которая теряет статическую функцию.

Ответ 8

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

Итак, я добавил следующий небольшой статический метод (хотел бы я сделать его методом расширения, но какого типа?):

public static List<T> CreateEmptyListOf<T>(Func<T> itemCreator)
{
    return Enumerable
        .Empty<object>()
        .Select(o => itemCreator())
        .ToList();
}

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

var emptyList = Ext.CreateEmptyListOf(() => new { Name = default(string), SomeInt = default(int) });