转换IEnumerable<;A>;到IEnumerable<;B>;而在编译时不知道类型

本文关键字:gt lt IEnumerable 编译 类型 不知道 转换 | 更新日期: 2023-09-27 18:22:03

我有两种类型:我们称它们为A和B。可以使用适配器方法将A转换为B。

然后,我在List<A>中有一个a的集合(它可以是任何支持IEnumerable<A>的集合类型)。

我现在想从IEnumerable<A>转换为IEnumerable<B>。我知道A和B各自的Type,我有一个将A转换为B的方法,但我的方法和/或类本身没有模板化,因此我无法访问模板类型;例如CCD_ 6中的T。

我实际上想写这个ConvertCollection方法,我知道"from"的类型是IEnumerable<{something}>:

object ConvertCollection(object from, Type fromType, Type toType, Converter converter);

我的转换器看起来像这样:

delegate object Converter(object from);

我的尝试把我留在这里:

object ConvertCollection(object from, Type fromType, Type toType, Converter converter)
{
    return ((IEnumerable<object>)from).Select(converter);
}

这部分起作用。如果我这样称呼它

ConvertCollection(new List<A>() { new A() }, typeof(A), typeof(B), AToBConverter);

返回的集合确实包含B的集合,但集合本身的类型是IEnumerable<object>,而不是IEnumerable<B>,因为我不知道如何转换为IEnumerable<{toType}>。(这很重要,因为结果需要序列化)。

我可以从另一端攻击它,并创建正确的返回类型,如下所示:

var result = Activator.CreateInstance(typeof(List<>).MakeGenericType(toType));
// TODO: populate result here
return result;

但问题是,要实现TODO部分,我需要调用List<>方法,但由于Co/ContraVariance规则,我不能将其强制转换为任何类型的List<>,所以即使我知道该类型支持List<>方法,我也无法使用它们来填充列表;例如使用Add()。

有没有一种方法可以在不使用"动态"和不进行过多反思的情况下做到这一点?我知道我可以通过反射找到并调用Add()方法,但似乎没有必要。

.NET 4.0 BTW

-澄清

正如Euphoric正确推测的那样,我试图传达以上内容,但非常失败,我在运行时知道类型A和B,但在编译时不知道它们。因此,直接使用泛型不是一种选择。我知道集合(提供的和必须返回的)实现了通用IEnumerable<>。这一切都是固定的,超出了我的控制范围。(我相应地调整了标题)。

**编辑2:修复了一些导致<>不显示的格式设置(很容易意外忽略反勾号!)

转换IEnumerable<;A>;到IEnumerable<;B>;而在编译时不知道类型

使用LINQ Select方法:

var result = listA.Select(a => Converter(a));

既然您使用的是.NET 4.0,那么您确实应该避免使用object并使用泛型。

我确定的解决方案是使用反射来调用Enumerable.Cast<>方法,将结果集合从IEnumerable<object>强制转换为正确的IEnumerable<>类型。我从这个问题的答案中得到了这个想法:将IEnumerable转换为IEnumerable<T>当T不是';直到运行时才知道。似乎只涉及很少的性能惩罚。

因此,完整的答案是:

object ConvertCollection(object from, Type fromType, Type toType, Converter converter)
{
    var partialResult = ((IEnumerable<object>)from).Select(converter);
    var castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(toType);
    return castMethod.Invoke(null, new[] { partialResult });
}

也许是这样的?

IEnumerable<TTo> ConvertCollection<TFrom,TTo>(object from, Converter converter)
{
    return ((IEnumerable<TFrom>)from).Select(a=>(TTo)converter(a)).ToList();
}

然后你可以简单地称之为:

ConvertCollection<A,B>(new List<A>() { new A() }, AToBConverter);