映射不同类型对象的通用方法

我想编写将List映射到新列表的Generic Method,类似于JS的map方法。 然后我会像这样使用这个方法:

var words= new List() { "Kočnica", "druga beseda", "tretja", "izbirni", "vodno bitje" }; List wordsMapped = words.Map(el => new { cela = el, končnica = el.Končnica(5) }); 

我知道有Select方法做同样的事情,但我需要编写自己的方法。 现在我有这个:

 public static IEnumerable SelectMy(this IEnumerable seznam, Predicate predicate) { List ret = new List(); foreach (var el in seznam) ret.Add(predicate(el)); return ret; } 

我也知道我可以使用yield return但我绝不能。 我认为问题是未声明的类型,编译器无法弄清楚它应该如何映射对象,但我不知道如何解决这个问题。 我发现所有示例和教程都是相同类型的map对象。

Linq的Select相当于其他函数语言中的map()函数。 映射函数通常不会被称为Predicate ,IMO – 谓词将是一个可以减少集合的filter。

您当然可以包含一个扩展方法,该方法将应用投影将输入映射到输出(其中任何一个都可以是匿名类型):

 public static IEnumerable Map(this IEnumerable seznam, Func mapper) { foreach (var item in seznam) yield return mapper(item); } 

这相当于

 public static IEnumerable Map(this IEnumerable seznam, Func mapper) { return seznam.Select(mapper); } 

如果您不想要强返回类型,可以将输出类型保留为object

 public static IEnumerable Map(this IEnumerable seznam, Func mapper) { // Same implementation as above 

并且这样称呼:

 var words = new List() { "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所以这排除了使用像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 { "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(); 

您可以修复代码以正常工作,如下所示:

 public static IEnumerable SelectMy(this IEnumerable seznam, Func mapping) { var ret = new List(); foreach (var el in seznam) { ret.Add(mapping(el)); } return ret; } 

请注意,与典型的Linq扩展相比,这是低效且有问题的,因为它一次枚举整个输入。 如果输入是一个无限系列,那么你将陷入困境。

可以在不使用yield情况下解决这个问题,但这有点冗长。 我想如果你能告诉我们你为什么要用双手绑在背后完成这项任务,那将是理想的。


作为奖励,以下是如何使用产量的惰性评估优势来实现这一点,而不实际使用产量。 这应该非常清楚地表明yield有多大:

 internal class SelectEnumerable : IEnumerable { private IEnumerable BaseCollection { get; set; } private Func Mapping { get; set; } internal SelectEnumerable(IEnumerable baseCollection, Func mapping) { BaseCollection = baseCollection; Mapping = mapping; } public IEnumerator GetEnumerator() { return new SelectEnumerator(BaseCollection.GetEnumerator(), Mapping); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } internal class SelectEnumerator : IEnumerator { private IEnumerator Enumerator { get; set; } private Func Mapping { get; set; } internal SelectEnumerator(IEnumerator enumerator, Func 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 MySelect( this IEnumerable enumerable, Func mapping) { return new SelectEnumerable(enumerable, mapping); } } 

您的代码的问题是Predicate是一个返回布尔值的委托,然后您尝试将其添加到List

使用Func可能就是你要找的东西。

话虽这么说,代码闻起来很糟糕:

  • 转换为object不太有用
  • 将映射T的委托传递给匿名类型将无济于事 – 您仍将获得一个没有有用属性的object
  • 您可能希望在方法中添加TResultgenerics类型参数,并将Func作为参数。