一行代码用于连接具有相同接口的两个不同对象列表

本文关键字:接口 两个 列表 对象 一行 代码 用于 连接 | 更新日期: 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);

它具有编译时安全性,如果FooBar中的任何一个没有实现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);
}

如果更改FooBar的定义,使其不再具有该接口,则会得到编译时错误。

问题似乎出在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上指定返回类型更简洁。