Построение общих списков анонимных типов и вложенных циклов - PullRequest
1 голос
/ 13 августа 2010

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

var myList = new List<???>();
foreach (object x in GetAllX())
{
    if (Process(x))
    {
        foreach (object y in GetAllY(x))
        {
            myList.Add(new {
                X = x,
                Y = y
            });
        }
    }
}

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

Обратите внимание, что я не могу изменить методы GetAllX и GetAllY.

Ответы [ 4 ]

4 голосов
/ 13 августа 2010

Простой ответ: «Вы не должны».

Существует трюк hacky , который позволяет вам сделать это:

var myList = new[] { new { X = (object) null, Y = (object) null } }.ToList();
myList.Clear();
foreach (object x in GetAllX())
    // ...

Но было бы более разумно использовать его так, как это было задумано:

var myList = GetAllX().Where(x => Process(x))
    .SelectMany(x => GetAllY(x).Select(y => new { X = x, Y = y }))
    .ToList();

Если вы действительно не можете использовать этот чисто функциональный стиль по какой-либо причине, или вы обнаружите, что должныСоздавая такой список в нескольких местах, вы, вероятно, должны объявить обычный класс вместо использования анонимного типа.Помните, что анонимные типы в любом случае компилируются в классы, поэтому анонимные типы не выигрывают в производительности, и даже преимущество читабельности / удобства сопровождения сомнительно, если вам приходится прибегать к хитростям, подобным хакерским в верхней части этого поста.

Некоторые люди предлагают использовать List<dynamic>, но я рекомендую против этого.Это сильно затрудняет ремонтопригодность, потому что имена и типы свойств больше не проверяются во время компиляции (вы можете набрать один из них и получить ошибку во время выполнения);это замедляет производительность во время выполнения, потому что каждый доступ проходит через динамический диспетчер;а также, когда вы помещаете свои объекты в этот список, вы в основном застреваете с их динамичностью, потому что вы не можете привести их обратно к анонимному типу.

1 голос
/ 13 августа 2010

Разве этот код не будет самым простым способом достижения желаемого результата?

var myList = (from x in GetAllX()
              where Process(x)
              from y in GetAllY(x)
              select new
              {
                  X = x,
                  Y = y,
              }).ToList();

(Тимви - я знаю, что это «linqified» версия вашего решения, но я подумал, что опубликую его, так как считаю, что в этом стиле его легко читать и следовать.)

0 голосов
/ 13 августа 2010

Поскольку вы помещаете X и Y в один и тот же список, они должны иметь общий базовый класс / интерфейс. Что? Нет отношений между ними? Тогда почему вы положили их в тот же список! Это не очень хорошая идея.

IEnumerable<BaseClass> AllXY = 
            GetAllX().Cast<BaseClass>().Union(GetAllY().Cast<BaseClass>());
foreach(var base in AllXY)
{
    //do something to base, usually with polymorphism
}
0 голосов
/ 13 августа 2010

У вас есть два варианта здесь.Во-первых, вы можете создать простой класс, который имеет свойства X и Y, и сделать свой список списком объектов этого класса, например так:

class NewClass
{
    public object X;
    public object Y;
}

var myList = new List<NewClass>();
foreach (object x in GetAllX())
{
    if (Process(x))
    {
        foreach (object y in GetAllY(x))
        {
            myList.Add(new NewClass() {
                X = x,
                Y = y
            });
        }
    }
}

Или вы можете просто использовать это вC # 4.0:

var myList = new List<dynamic>();
...