我想编写将List映射到新列表的Generic Method,类似于JS的map方法。然后我会像这样使用这个方法:
var words= new List<string>() { "Kočnica", "druga beseda", "tretja", "izbirni", "vodno bitje" };
List<object> wordsMapped = words.Map(el => new { cela = el, končnica = el.Končnica(5) });
我知道那里的Select方法做了同样的事情,但我需要编写自己的方法。现在我有这个:
public static IEnumerable<object> SelectMy<T>(this IEnumerable<T> seznam, Predicate<T> predicate)
{
List<object> ret = new List<object>();
foreach (var el in seznam)
ret.Add(predicate(el));
return ret;
}
我也知道我可以使用yield return
,但我不能再这样做了。我认为问题在于未声明的类型,编译器无法弄清楚它应该如何映射对象,但我不知道如何解决这个问题。所有示例和教程我都找到了相同类型的map对象。
答案 0 :(得分:2)
Linq的Select
相当于其他函数语言中的map()
函数。映射函数通常不会被称为Predicate
,IMO - 谓词将是一个可以减少集合的过滤器。
你当然可以包含一个扩展方法,该方法可以应用投影将输入映射到输出(其中任何一个都可以是匿名类型):
public static IEnumerable<TO> Map<TI, TO>(this IEnumerable<TI> seznam,
Func<TI, TO> mapper)
{
foreach (var item in seznam)
yield return mapper(item);
}
相当于
public static IEnumerable<TO> Map<TI, TO>(this IEnumerable<TI> seznam,
Func<TI, TO> mapper)
{
return seznam.Select(mapper);
}
如果您不想要强退货类型,可以将输出类型保留为object
public static IEnumerable<object> Map<TI>(this IEnumerable<TI> seznam, Func<TI, object> mapper)
{
// Same implementation as above
并且这样称呼:
var words = new List<string>() { "Kočnica", "druga beseda", "tretja", "izbirni", "vodno bitje" };
var wordsMapped = words.Map(el => new { cela = el, končnica = el.Končnica(5) });
修改强>
如果您喜欢动态语言的运行时惊险,您也可以使用dynamic
代替object
。
但是使用dynamic
这样的precludes the using the sugar扩展方法,例如Končnica
- Končnica
,或者需要成为所有类型的方法,或者是明确调用,例如
static class MyExtensions
{
public static int Končnica(this int i, int someInt)
{
return i;
}
public static Foo Končnica(this Foo f, int someInt)
{
return f;
}
public static string Končnica(this string s, int someInt)
{
return s;
}
}
然后,如果输入中的所有项目都已实现Končnica
,您可以调用:
var things = new List<object>
{
"Kočnica", "druga beseda",
53,
new Foo()
};
var mappedThings = things.Map(el => new
{
cela = el,
končnica = MyExtensions.Končnica(el, 5)
// Or el.Končnica(5) IFF it is a method on all types, else run time errors ...
})
.ToList();
答案 1 :(得分:2)
您可以修复代码,使其正常工作:
public static IEnumerable<TResult> SelectMy<T, TResult>(this IEnumerable<T> seznam,
Func<T, TResult> mapping)
{
var ret = new List<TResult>();
foreach (var el in seznam)
{
ret.Add(mapping(el));
}
return ret;
}
请注意,与典型的Linq扩展相比,这是低效且有问题的,因为它一次枚举整个输入。如果输入是一个无限系列,那么你将度过一个糟糕的时期。
可以在不使用yield
的情况下解决此问题,但这有点冗长。我认为如果你能告诉我们所有为什么你正试图用双手绑在背后完成这项任务,那将是理想的。
yield
的惰性评估优势实现此功能而不实际使用yield。这应该清楚地表明yield
的价值有多大:
internal class SelectEnumerable<TIn, TResult> : IEnumerable<TResult>
{
private IEnumerable<TIn> BaseCollection { get; set; }
private Func<TIn, TResult> Mapping { get; set; }
internal SelectEnumerable(IEnumerable<TIn> baseCollection,
Func<TIn, TResult> mapping)
{
BaseCollection = baseCollection;
Mapping = mapping;
}
public IEnumerator<TResult> GetEnumerator()
{
return new SelectEnumerator<TIn, TResult>(BaseCollection.GetEnumerator(),
Mapping);
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
internal class SelectEnumerator<TIn, TResult> : IEnumerator<TResult>
{
private IEnumerator<TIn> Enumerator { get; set; }
private Func<TIn, TResult> Mapping { get; set; }
internal SelectEnumerator(IEnumerator<TIn> enumerator,
Func<TIn, TResult> mapping)
{
Enumerator = enumerator;
Mapping = mapping;
}
public void Dispose() { Enumerator.Dispose(); }
public bool MoveNext() { return Enumerator.MoveNext(); }
public void Reset() { Enumerator.Reset(); }
public TResult Current { get { return Mapping(Enumerator.Current); } }
object IEnumerator.Current { get { return Current; } }
}
internal static class MyExtensions
{
internal static IEnumerable<TResult> MySelect<TIn, TResult>(
this IEnumerable<TIn> enumerable,
Func<TIn, TResult> mapping)
{
return new SelectEnumerable<TIn, TResult>(enumerable, mapping);
}
}
答案 2 :(得分:1)
您的代码存在的问题是Predicate<T>
是一个委托,它返回一个布尔值,然后您尝试添加到List<object>
。
使用Func<T,object>
可能是您正在寻找的。 p>
话虽如此,该代码闻起来很糟糕:
object
不太有用T
映射到匿名类型的代理人传递给他人不会有帮助 - 您仍然会收到object
后面没有任何有用属性的内容。TResult
泛型类型参数,并将Func<T, TResult>
作为参数。