【问题标题】:How to cast an object in generic collection?如何在通用集合中转换对象?
【发布时间】:2012-03-01 17:02:35
【问题描述】:

我需要将一个对象转换为一个泛型集合,看:

var currentEntityProperties = currentEntity.GetType().GetProperties();

foreach (var currentEntityProperty in currentEntityProperties)
{
    if (currentEntityProperty.PropertyType.GetInterfaces().Any(
                x => x.IsGenericType &&
                x.GetGenericTypeDefinition() == typeof(ICollection<>)))
    {
        var collectionType = currentEntityProperty.PropertyType.GetInterfaces().Where(
                                x => x.IsGenericType &&
                                x.GetGenericTypeDefinition() == typeof(ICollection<>)).First();

        var argumentType = collectionType.GetGenericArguments()[0];

        // now i need to convert the currentEntityProperty into a collection, something like that (this is wrong, so, what is thr right way?):
        var currentCollection = (ICollection<argumentType.GetType()>)currentEntityProperty.GetValue(currentEntity, null);
    }
}

我该怎么做?

Obs:我需要用这个集合调用另一个集合的 except 方法(这个集合我使用与 currentCollection 相同的方式获得,使用 anotherEntityProperty.GetValue(anotherEntity, null)

var itens = currentCollection.Except(anotherCollection);

【问题讨论】:

  • 之后你打算用它做什么?如果您能告诉我们更多关于更广泛背景的信息,我们或许可以提供另一种方法。
  • 你也不知道anotherCollection的类型吗?你从哪里得到的?你能在早些时候用反射进行一个通用调用吗?如果您使用的是 C# 4,动态类型也可以使这更容易。
  • anotherCollection 的类型我也不知道,但我知道 currentCollection 和 anotherCollection 将具有相同的类型。我得到另一个集合的方式与我得到 currentCollection 的方式相同。 var anotherCollection = newEntityProperty.GetValue(newEntity, null);.
  • @ViniciusOttoni 既然您显然精通反射,为什么不使用反射调用该方法?
  • @phoog 你的意思是,用反射调用 except 方法?

标签: c# reflection collections casting


【解决方案1】:

动态类型可以让编译器和 DLR 完成所有工作:

dynamic currentCollection = ...;
dynamic anotherCollection = ...;
dynamic items = Enumerable.Except(currentCollection, anotherCollection);

在执行时,它将为您完成所有反射工作并选择最合适的类型参数。

【讨论】:

  • 这太漂亮了! =P - 我不知道这个关键字dynamic。所以,谢谢乔恩!
  • @ViniciusOttoni:值得学习,以便您适度使用。请注意,如果“执行时编译器”无法确定要做什么,这将最终引发绑定异常。
  • 是的,我看到了如果不节制地使用它的危险,但如果你知道你在做什么,这可能是美丽的=)。
【解决方案2】:

对于Except扩展方法,你只需要一个IEnumerable&lt;T&gt;ICollection&lt;T&gt;实现了它。因此,您可以将您的属性转换为 IEnumerable&lt;object&gt;(至少如果 T 是引用类型)。

编辑:

如果你真的想,你可以试试这个通过反射在对象变量上调用例外:

// x and y are your object variables of the collections, 
// argumentType is the generic type you determined
  var methods = from m in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
              where m.ContainsGenericParameters
              && m.Name == "Except"
              && m.GetParameters().Count() == 2
              && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
              && m.ReturnType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
              select m;
  var method = methods.First();
  IEnumerable things = method.MakeGenericMethod(new Type[] { argumentType }).Invoke(null, new [] { x, y }) as IEnumerable;

【讨论】:

  • 非泛型ICollection不实现泛型IEnumerable&lt;T&gt;
  • 通用 ICollection 可以。括号不见了;)
  • @Botz3000 我这样做了,但我收到了这个错误:无法将“System.Collections.Generic.List1[System.String]”类型的对象转换为“System.Collections.Generic.ICollection@” 987654327@你说的可以吗?
  • 停止使用ICollection,你只需要IEnumerable。你从哪里得到这个错误?
【解决方案3】:

您不能使用ICollection&lt;some_type_determined_at_run_time&gt; - 编译器将无法为其生成任何有意义的代码。

如果类型与 ICollection&lt;Base_Class&gt; 相关的转换应该在 4.0 中工作 - 错误...这种转换适用于 IEnumerable(因为它具有“out T”作为类型),但不适用于 ICollection .

【讨论】:

  • Base_Class 是什么意思? Base_Class 是谁?因为我不知道argumentType会得到什么类型。
  • 我以某种方式决定,如果您的集合包含类型 Derived:MyBase ,那么您可以将其从 ICollection 转换为 ICollection ,这是不正确的 - 已编辑。
【解决方案4】:

这并没有直接回答你的问题,而是回答了我们在 cmets 中讨论的问题:

如何使用反射调用 except 方法?

Except 方法不是ICollection&lt;T&gt; 的成员,这就是您的GetMethod 调用返回null 的原因。相反,它是在静态类型System.Linq.Enumerable 上定义的扩展方法。要使用反射调用它,您必须将其视为静态方法。像这样的:

// there are two overloads of Except and I'm too lazy to construct the type parameters to get the correct overload
var methods = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public);
var exceptMethod = methods.Where(m => m.Name.Equals("Except") && m.GetParameters().Lengh == 2).Single();

object[] arguments = new [] { currentCollection, anotherCollection };
object items = exceptMethod.Invoke(null, arguments);

这是 Jon Skeet 的“简单方法”(C# 4.0 及更高版本):

dynamic items = Enumerable.Except((dynamic)currentCollection, (dynamic)anotherCollection);

【讨论】:

  • @ViniciusOttoni 我忘记在复制粘贴后更改 BindingFlags 参数。现在已经修好了。我也忘记添加对Single()的调用,这也是固定的。
猜你喜欢
  • 2010-12-15
  • 1970-01-01
  • 2019-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-31
相关资源
最近更新 更多