一行代码用于连接具有相同接口的两个不同对象列表
本文关键字:接口 两个 列表 对象 一行 代码 用于 连接 | 更新日期: 2023-09-27 18:08:48
考虑下面的简单代码:
public interface Iinterface { }
public class Foo : Iinterface { }
public class Bar : Iinterface { }
[TestMethod()]
public void Test_Concat()
{
var bars = new List<Bar>();
var foos = new List<Foo>();
// Ok
IEnumerable<Iinterface> concats1 = bars.Cast<Iinterface>().Concat(foos.Cast<Iinterface>());
// Compilation error
IEnumerable<Iinterface> concats2 = bars.Concat(foos);
}
我想在一行中将两个列表连接在一起,并在编译时保持类型安全。
例如,如果我删除类Foo
的接口,这仍然可以编译,但在运行时失败:
public interface Iinterface { }
public class Foo { }
public class Bar : Iinterface { }
[TestMethod()]
public void Test_Concat()
{
var bars = new List<Bar>();
var foos = new List<Foo>();
IEnumerable<Iinterface> concats1 = bars.Cast<Iinterface>().Concat(foos.Cast<Iinterface>());
}
如果我使用OfType<T>()
,这将不会在运行时失败,但我希望它在编译时失败。我能找到的最好的方法是使用这3行代码:
var list = new List<Iinterface>();
list.AddRange(bars);
list.AddRange(foos);
但是对于这么简单的事情,我想找到一个一行代码,如果可能的话,检索IEnumerable<Iinterface>
而不是List<Iinterface>
。
有什么办法可以做到这一点吗?
你可以自己写方法吗?
因为IEnumerable是协变的,你只需要指定基
var list= Combine<IInterface>(foos, bars);
private IEnumerable<T> Combine<T>(IEnumerable<T> ListA, IEnumerable<T> ListB)
{
foreach (T t in ListA)
yield return t;
foreach (T t in ListB)
yield return t;
}
尽管你不妨直接写
var list= foos.Concat<IInterface>(bars);
如何使您的自定义Concat
扩展方法版本:
public static class MyEnumerable
{
public static IEnumerable<T> Concat<T, T1, T2>(this IEnumerable<T1> source, this IEnumerable<T2> other)
where T1 : T
where T2 : T
{
return source.Cast<T>().Concat(other.Cast<T>());
}
}
用法:
var items = foos.Concat<Iinterface, Foo, Bar>(bars);
它具有编译时安全性,如果Foo
和Bar
中的任何一个没有实现Iinterface
,则不会编译。
它还应该支持开箱即用的不同执行,因为使用的LINQ方法做到了。
事实上,concat方法已经完成了我想要的,我只需要通过指定返回类型来提供一点帮助,因为对返回类型没有类型推断。
public interface Iinterface { }
public class Foo : Iinterface { }
public class Bar : Iinterface { }
[TestMethod()]
public void Test_Concat()
{
var bars = new List<Bar>();
var foos = new List<Foo>();
var list = foos.Concat<Iinterface>(bars);
}
我太习惯使用推理了,以至于我忘了有时候编译器需要提示!
你可以这样做:
public void Test_Concat2()
{
IEnumerable<Iinterface> bars = new List<Bar>();
IEnumerable<Iinterface> foos = new List<Foo>();
IEnumerable<Iinterface> concats = bars.Concat(foos);
}
如果更改Foo
或Bar
的定义,使其不再具有该接口,则会得到编译时错误。
问题似乎出在Cast而不是Concat上。声明如下:
public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second);
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source);
在Concat中,这两个可赋值都是强类型的,但Cast不约束源类型,因此问题发生在运行时而不是编译时。
在(result) s 运行时,尝试如下MyCast实现失败public static IEnumerable<TResult> MyCast<TResult>(this IEnumerable source)
{
var result = (from object s in source select (TResult) s).ToList();
return result.AsEnumerable();
}
如果在新的类型转换中实现一点类型检查,如下所示:
public static IEnumerable<TResult> MyCast2<TSource, TResult>(this IEnumerable<TSource> source)
where TSource : TResult
{
var result = (from object s in source select (TResult)s).ToList();
return result.AsEnumerable();
}
则调用行需要两个类型参数,如
IEnumerable<Iinterface> concats3 = bars.Concat(foos.MyCast2<Foo, Iinterface>());
,编译器检测到Foo不能转换为Iinterface。
当然,在Concat上指定返回类型更简洁。