1

考虑这个简单的代码:

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);
}

我想将两个列表合并为一行,并在编译时保持类型安全

例如,如果我删除了 class 的接口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>.
有什么办法可以做到这一点?

4

5 回答 5

3

你可以写你自己的方法吗?

由于 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);
于 2013-04-11T07:37:45.767 回答
2

如何制作您的自定义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 方法可以。

于 2013-04-11T07:19:43.320 回答
2

事实上,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);
    }

我太习惯使用推理了,以至于有时我忘记了,编译器需要提示!

于 2013-04-11T07:45:57.827 回答
0

你可以这样做:

public void Test_Concat2()
{
    IEnumerable<Iinterface> bars = new List<Bar>();
    IEnumerable<Iinterface> foos = new List<Foo>();

    IEnumerable<Iinterface> concats = bars.Concat(foos);
}

如果您随后更改定义FooBar使其不再具有接口,您将收到编译时错误。

于 2013-04-11T07:41:24.030 回答
0

问题似乎出在 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 中,两个 enunerable 都是强类型的,但 Cast 不限制源类型,因此问题发生在运行时而不是编译时。

在(TResult) 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();
}

如果在新的 Cast 中实现一些类型检查,如下所示:

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 上指定返回类型更简洁。

于 2013-04-11T09:14:08.043 回答