【问题标题】:I need a Linq IEnumerable<>.Cast(typeof(T))我需要一个 Linq IEnumerable<>.Cast(typeof(T))
【发布时间】:2014-03-01 15:07:57
【问题描述】:

我正在查询一个数据结构,结果类型是IEnumerable&lt;IEntry&gt;IEntry 是一个框架接口),每个条目都有一个Data 属性(对象类型),这对我来说很有趣。

我的代码如下所示:

var resultList = framework.QueryAllOfType(queryClause.Type)
                          .Select(e => e.Data)
                          .ToList();
deleagte.DynamicInvoke(new[]{resultList});

委托背后的方法如下所示:

void Foo (IEnumerable<SomeType> bar); // if queryClause.Type == typeof(SomeType)
void Foo (IEnumerable<OtherType> bar); // if queryClause.Type == typeof(OtherType)

我非常肯定queryClause.Type 匹配SomeType,当然,.NET 框架不是 ;-)

不幸的是,这意味着 resultList 的类型为 IEnumerable&lt;object&gt;,尽管其中的所有对象都是正确的类型,但我无法调用委托(例外:IEnumerable&lt;object&gt; cannot be converted into IEnumerable&lt;SomeType&gt;)。

我知道为什么会这样,但解决办法是什么?我需要一些类似的东西:

.Select(e =&gt; e.Data).Cast(queryClause.Type).ToList() 应该返回一个IEnumerable&lt;queryClause.Type&gt;。 .NET 框架中是否已经存在这样的东西?有没有更好的解决方案?

重要提示:由于两个答案已经误解了我的意图,因此我不能将该类型用作泛型参数,因为它仅在运行时才知道。因此所有Cast&lt;...&gt;()Select(e =e as ...)等都不起作用。

【问题讨论】:

  • 我不明白:您有一个不接受 IEnumerable 但需要另一种类型的委托,但您声称该类型不是编译时间?代表是怎么知道的?
  • 当然还有更多的委托——都有不同的参数类型(这就是为什么我需要 DynamicInvoke 来调用它们)。

标签: c# .net linq generics ienumerable


【解决方案1】:

您可以使用反射调用Cast

var method = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(queryClause.Type);
IEnumerable<object> cSeq = (IEnumerable<object>)method.Invoke(null, new object[] { resultList });

deleagte.DynamicInvoke(new[]{ cSeq });

【讨论】:

  • 好的,谢谢。与此同时,我自己也找到了解决方案。希望 .NET 框架中已经有一些东西。
  • 这不起作用,因为在调用Cast() 后,您需要再次调用ToList() 才能调用委托,这将再次返回IList&lt;object&gt;
  • 您必须使用 MakeGenericMethod 关键字将 Cast/ToList 组合到一个调用中,我现在在另一个线程中找到了这个答案:stackoverflow.com/a/12848842/1400869 这似乎是一个可行的解决方案。跨度>
  • @D.R. - 您的问题是代表接受 IEnumerable&lt;T&gt; 而不是 List&lt;T&gt; 所以这应该有效。如果您需要传递List&lt;T&gt;,那么您必须使用反射调用ToList&lt;T&gt;,方法与Cast&lt;T&gt; 相同。
  • 是的,我会接受你的回答。只是把链接留在这里,因为可能其他人也会绊倒它。
【解决方案2】:

根据您的反馈并使用其他答案,我创建了一个 ToListOfType() 扩展方法,应该可以满足您的需求。

public static class EnumerableExtensions
{
    private static readonly Type _enumerableType = typeof(Enumerable);

    public static IEnumerable CastAsType(this IEnumerable source, Type targetType)
    {
        var castMethod = _enumerableType.GetMethod("Cast").MakeGenericMethod(targetType);

        return (IEnumerable)castMethod.Invoke(null, new object[] { source });
    } 

    public static IList ToListOfType(this IEnumerable source, Type targetType)
    {
        var enumerable = CastAsType(source, targetType);

        var listMethod = _enumerableType.GetMethod("ToList").MakeGenericMethod(targetType);

        return (IList)listMethod.Invoke(null, new object[] { enumerable });
    } 
}

如果您只需将 ToList() 调用替换为 ToListOfType(queryClause.Type),这应该适用于您的委托。我实现了CastAsTypeToListOfType 方法,因此如果您愿意,您可以不迭代集合。我会提醒未来的读者,这些仅在您将结果动态传递给委托的情况下才有用 - 在所有其他情况下,Cast&lt;T&gt; 操作将是首选。

【讨论】:

  • 这对 ToList() 的行为没有任何影响。 e.Data 已经是 queryClause.Type 类型,但是编译器不知道,这就是为什么 .ToList() 返回 IList&lt;object&gt;
  • @D.R. - 使用扩展方法进行了更新,这些方法集成了已接受的答案,而无需您在 cmets 中引用的答案所需的额外的每类方法。
【解决方案3】:

尝试以类似的方式结束:

.ToList<IEntry>()

例如,如果您执行以下操作:

int[] 文本 = {01, 2, 3, 4, 5};

List<string> list = text.Select(x => x.ToString()).ToList<string>();

【讨论】:

  • 我已经更新了我的问题,这对我来说是不可能的解决方案。
  • 好的!很抱歉,伙计! ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-17
  • 1970-01-01
  • 1970-01-01
  • 2014-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多