LINQ: Как объявить IEnumerable [AnonymousType]? - PullRequest
24 голосов
/ 23 мая 2009

Это моя функция:

    private IEnumerable<string> SeachItem(int[] ItemIds)
    {
        using (var reader = File.OpenText(Application.StartupPath + @"\temp\A_A.tmp"))
        {
            var myLine = from line in ReadLines(reader)
                         where line.Length > 1
                         let id = int.Parse(line.Split('\t')[1])
                         where ItemIds.Contains(id)
                         let m = Regex.Match(line, @"^\d+\t(\d+)\t.+?\t(item\\[^\t]+\.ddj)")
                         where m.Success == true
                         select new { Text = line, ItemId = id, Path = m.Groups[2].Value };
            return myLine;
        }
    }

Я получаю ошибку компиляции, потому что "myLine" не является IEnumerable [string], и я не знаю, как написать IEnumerable [Anonymous]

"Не удалось неявно преобразовать тип 'System.Collections.Generic.IEnumerable [AnonymousType # 1]' в 'System.Collections.Generic.IEnumerable [string]'"

Ответы [ 5 ]

15 голосов
/ 23 мая 2009

Вы не можете объявить IEnumerable<AnonymousType>, потому что тип не имеет (известного) имени во время сборки. Поэтому, если вы хотите использовать этот тип в объявлении функции, сделайте его нормальным типом. Или просто измените ваш запрос, чтобы он возвращал IENumerable<String>, и придерживайтесь этого типа.

Или верните IEnumerable<KeyValuePair<Int32, String>>, используя следующую инструкцию выбора.

select new KeyValuePair<Int32, String>(id, m.Groups[2].Value)
10 голосов
/ 23 мая 2009

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

1) измените сигнатуру вашего метода, чтобы получить IEnumerable (не универсальную)

2) добавить приведение по примеру помощник:

public static class Extensions{
    public static IEnumerable<T> CastByExample<T>(
            this IEnumerable sequence, 
            T example) where T: class
    {
        foreach (Object o in sequence)
            yield return o as T;
    }
}

3) затем вызовите метод примерно так:

var example = new { Text = "", ItemId = 0, Path = "" };
foreach (var x in SeachItem(ids).CastByExample(example))
{
    // now you can access the properties of x 
    Console.WriteLine("{0},{1},{2}", x.Text, x.ItemId, x.Path);
}

И все готово.

Ключом к этому является тот факт, что если вы создадите анонимный тип с одинаковым порядком, типами и именами свойств в двух местах, типы будут использоваться повторно. Зная это, вы можете использовать дженерики, чтобы избежать рефлексии.

Надеюсь, это поможет Alex

9 голосов
/ 23 мая 2009

Подпись метода на SearchItem указывает, что метод возвращает IEnumerable<string>, но анонимный тип, объявленный в вашем запросе LINQ, не относится к типу string. Если вы хотите сохранить одну и ту же сигнатуру метода, вам нужно изменить свой запрос, чтобы выбрать только string s. например,

return myLine.Select(a => a.Text);

Если вы настаиваете на возврате данных, выбранных по вашему запросу, вы можете вернуть IEnumerable<object>, если вы замените свой return оператор на

return myLine.Cast<object>();

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

Но на самом деле, если вы собираетесь использовать анонимный тип вне метода, в котором он объявлен, вы должны определить класс и метод должен возвращать IEnumerable этого класса. Анонимные типы удобны, но могут подвергаться злоупотреблениям.

5 голосов
/ 23 мая 2009

Ваша функция пытается вернуть IEnumerable , когда выполняемый оператор LINQ фактически возвращает IEnumerable , где T - это тип, сгенерированный во время компиляции. Анонимные типы не всегда являются анонимными, так как они принимают конкретный конкретный тип после компиляции кода.

Однако анонимные типы, поскольку они эфемерны до тех пор, пока не скомпилированы, могут использоваться только в той области, в которой они созданы. Чтобы удовлетворить ваши потребности в приведенном вами примере, я бы сказал, что самое простое решение - создать простую сущность, хранит результаты вашего запроса:

public class SearchItemResult
{
    public string Text { get; set; }
    public int ItemId { get; set; }
    public string Path { get; set; }
}

public IEnumerable<SearchItemResult> SearchItem(int[] itemIds)
{
    // ...
    IEnumerable<SearchItemResult> results = from ... select new SearchItemResult { ... }
}

Однако, если ваша конечная цель не состоит в том, чтобы получить какой-либо объект, и вас интересует, скажем, Путь ... тогда вы все равно можете сгенерировать IEnumerable :

IEnumerable<string> lines = from ... select m.Groups[2].Value;

Надеюсь, это поможет прояснить ваше понимание LINQ, перечислимых и анонимных типов. :)

0 голосов
/ 23 мая 2009

Следует помнить, что LINQ операторы используют отложенное выполнение . Это означает, что ваш оператор LINQ фактически не выполняется, пока вы либо не выполните итерацию по нему в операторе foreach, либо не вызовете метод .ToList() для myLine.
Для вашего примера попробуйте изменить:

return myLine;

Кому:

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