【问题标题】:Casting an object to a base collection将对象强制转换为基础集合
【发布时间】:2011-02-17 15:07:17
【问题描述】:

我并不完全相信这是可能的,但是就这样吧。我有一个返回对象的方法,尽管实际类型是 Collection。现在,我可以轻松地将对象投射到集合中使用

var myCollection = myObject as Collection<MyClassA>;

但是我遇到的问题是Collection&lt;MyClassA&gt; 也可以是Collection&lt;MyClassB&gt;Collection&lt;MyClassC&gt;。所有这些MyClassX 都继承自MyBaseClass,所以理想情况下我希望能够做类似的事情

var myCollection = myObject as Collection<MyBaseClass>;

但是,这会在强制转换时引发异常。无论如何都可以这样做吗?我知道它可能在 .Net 4 内?

感谢您的帮助。

编辑: 好的 - 到目前为止的答案非常有用,但是它们只解决了解决方案的第二部分 - 转换/转换集合。

我仍然不确定最初应该如何将对象转换为集合(不为每种可能的类型使用巨大的 if 语句)

【问题讨论】:

  • 这是 C# 4.0 的特性,与 .NET 4.0 无关。
  • @John:无论是 .NET4 还是 C#4 的功能,您都无法转换为 Collection&lt;MyBaseClass&gt;
  • Casting List<T> - covariance/contravariance problem 的可能副本。请阅读 Jon Skeet 的回答,它将完全符合您的情况。
  • @LukeH:我知道协变和逆变只适用于接口和委托。当我说“它是”一个 C# 4.0 特性时,我假设 OP 正在考虑 C# 4.0 的协变和逆变特性,它们不是 .NET 特性。
  • @John:我以为你会意识到这一点。我的评论实际上只是为了 OP 的利益,以防他们误解您的评论,说在 C#4 中可以转换为 Collection&lt;MyBaseClass&gt;。但是(这是一个真正的挑剔)自从 C#4 作为 .NET4 的一部分发布以来,我可以合理地说 C#4 中的差异是 .NET4 功能集的一部分。

标签: c# .net


【解决方案1】:

只有 .NET 4 中的 IEnumerable&lt;T&gt; 支持此功能。查看签名的差异:

IEnumerable&lt;T&gt;:

public interface IEnumerable<out T> : IEnumerable

Collection&lt;T&gt;:

public class Collection<T> : IList<T>, 
ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable

类型参数中的 out 关键字告诉 .NET 支持变化。

【讨论】:

  • 我认为使用 .OfType() 和通用接口也适用于 .NET 4.0 之前的用户。
  • 感谢这部分答案。我仍然遇到的问题是,一开始我有一个未知泛型类型的基本对象 - 我仍然需要先将对象转换为 Collection 或 IEnumerable?理想情况下,我需要将对象直接从偏移量转换为 IEnumerable
  • @code4life, Cast&lt;T&gt;() 将是MyClassIMyClass 转换的首选途径。你的意图更明显。当您在转换之前确实需要过滤时使用OfType&lt;T&gt;(),例如混合源。
【解决方案2】:

在我访问 .NET 4 之前,我编写了一个扩展方法来实现这一点:

public static IEnumerable<U> CastCollection<T, U>(this IList<T> items) where U : class
{
   var collection = new List<U>();
   foreach (var item in items)
   {
      if (item is U)
      {
           var newItem = item as U;
           collection.Add(newItem);
      }
  }
  return collection;
}

你会这样使用它:

var myCollection = myObject.CastCollection<MyClassA, MyBaseClass>();

myCollection 在这种情况下将是 IEnumerable&lt;MyBaseClass&gt;

【讨论】:

  • 在这种方法中,您会默默地丢弃无法施放的项目,从名称上看这是不直观的。你写的基本上是重新发明OfType&lt;T&gt;,它将过滤现有序列以返回和转换与给定类型匹配的项目。你的名字所暗示的就是Cast&lt;T&gt; 的重新发明,如果某些东西无法转换,它会抛出异常。
  • 该方法固然有缺陷,但符合项目要求。我不知道OfType&lt;T&gt; 的用途,否则我会使用它。不过,我很高兴您指出了它的缺陷,谢谢。
  • 我自己重新发明了很多轮子。在有人将我指向原版之前,我总是为自己感到自豪。
  • @Anthony 喜欢构建一个功能齐全、可插入的 JSON 序列化器-反序列化器?我当了两天的摇滚明星。哎呀。 ;)
【解决方案3】:

替代解决方案:您可以使用接口和泛型来获得您想要的。

public interface IMyClass
{
}

public class MyClassA : IMyClass
{
}

public class MyClassB : IMyClass
{
}

public class MyClassC : IMyClass
{
}

static void Main(string[] args)
{
    var listA = new List<IMyClass>{new MyClassA{}, new MyClassA{}};
    var listB = new List<IMyClass> { new MyClassB { }, new MyClassB { } };
    var listC = new List<IMyClass> { new MyClassC { }, new MyClassC { } };

    List<IMyClass> genericList = listA.Cast<IMyClass>().ToList();
}

这样的东西可以正确编译,还允许您将实现公共接口的任何类型的不同列表分配给同一个变量(在本例中为 genericList

【讨论】:

    【解决方案4】:

    这不能通过将集合作为一个整体进行转换来完成。但是,您可以将单个元素转换为新集合。看看 LINQ 的 Cast 扩展方法。

    【讨论】:

      猜你喜欢
      • 2017-01-20
      • 2011-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-30
      • 1970-01-01
      相关资源
      最近更新 更多