LINQ: Как объявить IEnumerable [AnonymousType]?
Это моя функция:
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]'"
Ответы
Ответ 1
Вы не можете объявить IEnumerable<AnonymousType>
, потому что тип не имеет имени (имени) во время сборки. Поэтому, если вы хотите использовать этот тип в объявлении функции, сделайте его обычным типом. Или просто измените свой запрос, чтобы вернуть IENumerable<String>
и придерживаться этого типа.
Или верните IEnumerable<KeyValuePair<Int32, String>>
, используя следующий оператор select.
select new KeyValuePair<Int32, String>(id, m.Groups[2].Value)
Ответ 2
Подпись метода на SearchItem
указывает, что метод возвращает IEnumerable<string>
, но анонимный тип, объявленный в вашем запросе LINQ, не имеет типа string
. Если вы хотите сохранить одну и ту же подпись метода, вам нужно изменить свой запрос, чтобы выбрать только string
s. например.
return myLine.Select(a => a.Text);
Если вы настаиваете на возврате данных, выбранных по вашему запросу, вы можете вернуть IEnumerable<object>
, если вы замените инструкцию return
на
return myLine.Cast<object>();
Затем вы можете использовать объекты с помощью отражения.
Но действительно, если вы собираетесь использовать анонимный тип вне метода, в котором он объявлен, вы должны определить класс a, если метод возвращает IEnumerable
этого класса. Анонимные типы - это удобство, но они подвержены злоупотреблениям.
Ответ 3
Я не обязательно рекомендую это...
Это своего рода подрывная система типа, но вы можете это сделать:
1) измените вашу подпись метода, чтобы вернуть IEnumerable
(не общий)
2) добавьте cast by example helper:
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
Ответ 4
Ваша функция пытается вернуть IEnumerable <string> , когда оператор LINQ, который вы выполняете, фактически возвращает IEnumerable <T> где 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 { ... }
}
Однако, если ваша конечная цель - не извлекать какой-либо объект, и вас интересует только, скажем, Path..., то вы все равно можете создать IEnumerable <string> :
IEnumerable<string> lines = from ... select m.Groups[2].Value;
Я надеюсь, что это поможет прояснить ваше понимание LINQ, перечислений и анонимных типов.:)
Ответ 5
Следует помнить, что операторы LINQ
используют отложенное исполнение. Это означает, что ваш оператор LINQ
фактически не выполняется до тех пор, пока вы не перейдете к нему в инструкции foreach
или вы вызовете метод .ToList()
на myLine
.
Для вашего примера попробуйте изменить:
return myLine;
To:
return myLine.ToList();
Ответ 6
Не правильный ответ на вопрос, но я надеюсь, что кто-то найдет его полезным, потому что я сначала добрался до этого вопроса, когда искал ответ на свой.
Я только что нашел один интересный способ объявить анонимный IEnumerable.
//Declaration. The type of newEnumerable is IEnumerable<'a>
var newEnumerable = new string[] { }.Select(x => new {SomeInt = 0, SomeString = ""});
Таким образом, использование этого будет выглядеть так...
//Adding value
newEnumerable = someOtherEnumerable.Select(x => new {x.SomeInt, x.SomeString});
//Adding multiple values via concatanation
someAnotherEnumerable.ForEach(x =>
{
newEnumerable = newEnumerable.Concat(new []{
new {x.SomeInt, x.SomeString}
});
});