Ответ 1
Самый простой способ написать любой итератор - с помощью блока итератора, например:
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);
}