Самый простой способ написать любой итератор - использовать блок итератора, например:
static IEnumerable<T> Where<T>(this IEnumerable<T> data, Func<T, bool> predicate)
{
foreach(T value in data)
{
if(predicate(value)) yield return value;
}
}
Ключом здесь является "yield return
", который превращает метод в блок итератора, а компилятор генерирует перечислитель (IEnumerator<T>
), который делает то же самое. При вызове логический вывод типа автоматически обрабатывает T
, поэтому вам просто нужно:
int[] data = {1,2,3,4,5};
var odd = data.Where(i=>i%2 != 0);
Вышесказанное можно использовать с анонимными типами просто отлично.
Вы можете, конечно, указать T
, если хотите (если он не анонимный):
var odd = data.Where<int>(i=>i%2 != 0);
Re IEnumerable
(неуниверсальный), ну, простейший подход для вызывающего абонента - использовать .Cast<T>(...)
или .OfType<T>(...)
, чтобы сначала получить IEnumerable<T>
. Вы можете передать this IEnumerable
в приведенном выше примере, но вызывающая сторона должна сама указать T
, вместо того чтобы компилятор выводил ее. Вы не можете использовать это, когда T
является анонимным типом, поэтому мораль здесь такова: не используйте неуниверсальную форму IEnumerable
с анонимными типами.
Существуют несколько более сложные сценарии, в которых сигнатура метода такова, что компилятор не может идентифицировать T
(и, конечно, вы не можете указать его для анонимных типов). В этих случаях, как правило, можно перефакторизовать в другую сигнатуру, которую компилятор может использовать с логическим выводом (возможно, с помощью метода pass-thru), но вам потребуется опубликовать реальный код, чтобы обеспечить ответ здесь.
(обновлено)
После обсуждения вот способ использования Cast<T>
с анонимными типами. Ключ должен предоставить аргумент, который может использоваться для вывода типа (даже если аргумент никогда не используется). Например:
static void Main()
{
IEnumerable data = new[] { new { Foo = "abc" }, new { Foo = "def" }, new { Foo = "ghi" } };
var typed = data.Cast(() => new { Foo = "never used" });
foreach (var item in typed)
{
Console.WriteLine(item.Foo);
}
}
// note that the template is not used, and we never need to pass one in...
public static IEnumerable<T> Cast<T>(this IEnumerable source, Func<T> template)
{
return Enumerable.Cast<T>(source);
}