【问题标题】:C# Casting a List<ObjBase> as List<Obj>C# 将 List<ObjBase> 转换为 List<Obj>
【发布时间】:2010-11-18 22:57:59
【问题描述】:

为什么我不能将List&lt;ObjBase&gt; 转换为List&lt;Obj&gt;?为什么以下不起作用:

internal class ObjBase
   {
   }

internal class Obj : ObjBase
   {
   }   

internal class ObjManager
{
    internal List<Obj> returnStuff()
    {
       return getSomeStuff() as List<Obj>;
    }

    private List<ObjBase> getSomeStuff()
    {
       return new List<ObjBase>();
    }

}

相反,我必须这样做:

internal class ObjBase
   {
   }

internal class Obj : ObjBase
   {
   }

internal class ObjManager
{
    internal List<Obj> returnStuff()
    {
       List<ObjBase> returnedList = getSomeStuff();
       List<Obj> listToReturn = new List<Obj>(returnedList.Count);
       foreach (ObjBase currentBaseObject in returnedList)
       {
          listToReturn.Add(currentBaseObject as Obj);
       }
       return listToReturn;
    }

    private List<ObjBase> getSomeStuff()
    {
       return new List<ObjBase>();
    }
}

我在 Visual Studio 2008 中收到以下错误(为便于阅读而缩短):

无法通过引用转换、装箱转换、拆箱转换、包装转换或空类型转换将类型“列表”转换为“列表”

谢谢。

【问题讨论】:

标签: c# generics inheritance list casting


【解决方案1】:

您可以使用 System.Linq 中的 CastToList 扩展方法将其放在一行中。

代替

internal List<Obj> returnStuff()
{
   return getSomeStuff() as List<Obj>;
}

这样做:

internal List<Obj> returnStuff()
{
   return getSomeStuff().Cast<Obj>().ToList();
}

【讨论】:

    【解决方案2】:

    我只能从 Java 的角度描述“问题”,但据我所知,这方面在 C# 和 Java 中是相同的:

    List&lt;ObjBase&gt; 不是List&lt;Obj&gt;,因为它可能包含不是Obj 对象的ObjBase 对象。

    List&lt;Obj&gt; 的另一种方式不能转换为 List&lt;ObjBase&gt;,因为前者保证接受带有 ObjBase 参数的 Add() 调用,后者不会接受!

    总结一下:即使Obj 是-a ObjBase List&lt;Obj&gt; 不是 List&lt;ObjBase&gt;

    【讨论】:

    • 这是我读过的通用协变问题的最简单描述。不错。
    【解决方案3】:
    【解决方案4】:
    【解决方案5】:

    list.ConvertAll 看起来很诱人,但有一个很大的缺点:它会创建一个全新的列表。这将影响性能和内存使用,尤其是对于大型列表。

    通过更多的努力,您可以创建一个包装器列表类,将原始列表保留为内部引用,并仅在使用时转换项目。

    用法:

    var x = new List<ObjBase>();
    var y = x.CastList<ObjBase, Obj>(); // y is now an IList<Obj>
    

    要添加到库中的代码:

    public static class Extensions
    {
        public static IList<TTo> CastList<TFrom, TTo>(this IList<TFrom> list)
        {
            return new CastedList<TTo, TFrom>(list);
        }
    }
    
    public class CastedList<TTo, TFrom> : IList<TTo>
    {
        public IList<TFrom> BaseList;
    
        public CastedList(IList<TFrom> baseList)
        {
            BaseList = baseList;
        }
    
        // IEnumerable
        IEnumerator IEnumerable.GetEnumerator() { return BaseList.GetEnumerator(); }
    
        // IEnumerable<>
        public IEnumerator<TTo> GetEnumerator() { return new CastedEnumerator<TTo, TFrom>(BaseList.GetEnumerator()); }
    
        // ICollection
        public int Count { get { return BaseList.Count; } }
        public bool IsReadOnly { get { return BaseList.IsReadOnly; } }
        public void Add(TTo item) { BaseList.Add((TFrom)(object)item); }
        public void Clear() { BaseList.Clear(); }
        public bool Contains(TTo item) { return BaseList.Contains((TFrom)(object)item); }
        public void CopyTo(TTo[] array, int arrayIndex) { BaseList.CopyTo((TFrom[])(object)array, arrayIndex); }
        public bool Remove(TTo item) { return BaseList.Remove((TFrom)(object)item); }
    
        // IList
        public TTo this[int index]
        {
            get { return (TTo)(object)BaseList[index]; }
            set { BaseList[index] = (TFrom)(object)value; }
        }
    
        public int IndexOf(TTo item) { return BaseList.IndexOf((TFrom)(object)item); }
        public void Insert(int index, TTo item) { BaseList.Insert(index, (TFrom)(object)item); }
        public void RemoveAt(int index) { BaseList.RemoveAt(index); }
    }
    
    public class CastedEnumerator<TTo, TFrom> : IEnumerator<TTo>
    {
        public IEnumerator<TFrom> BaseEnumerator;
    
        public CastedEnumerator(IEnumerator<TFrom> baseEnumerator)
        {
            BaseEnumerator = baseEnumerator;
        }
    
        // IDisposable
        public void Dispose() { BaseEnumerator.Dispose(); }
    
        // IEnumerator
        object IEnumerator.Current { get { return BaseEnumerator.Current; } }
        public bool MoveNext() { return BaseEnumerator.MoveNext(); }
        public void Reset() { BaseEnumerator.Reset(); }
    
        // IEnumerator<>
        public TTo Current { get { return (TTo)(object)BaseEnumerator.Current; } }
    }
    

    【讨论】:

      【解决方案6】:

      我认为你误解了你想要做的演员表。您认为您正在更改存储在列表中的对象的类型,而实际上您是在尝试更改列表本身的类型。您不能更改列表本身,因为您已经填充了它,这是有道理的。

      您可以将其视为基类的列表,然后在处理列表项时对其进行转换,这将是我的方法。

      这次尝试演员的目的是什么?

      【讨论】:

        【解决方案7】:

        C# 目前不支持泛型类型的变体。根据我的阅读,这将在 4.0 中发生变化。

        有关泛型差异的更多信息,请参阅here

        【讨论】:

          【解决方案8】:

          Linq 有一个 ConvertAll 方法。所以像

          list.ConvertAll<Obj>(objBase => objbase.ConvertTo(obj));
          

          我不确定还有什么建议。我假设 ObjBase 是基类,如果所有 ObjBase 对象都是 Obj 对象,我不确定你为什么首先要有这两个对象。可能我跑题了。

          编辑:list.Cast 方法会比上面的方法更好,假设它们可以相互转换。在我阅读其他答案之前忘记了这一点。

          【讨论】:

            【解决方案9】:

            这是 C# 的一大痛点——这就是泛型的设计方式。 List 没有扩展 List,它只是一种完全不同的类型。您不能以任何方式将它们相互转换或分配,您唯一的选择是将一个列表复制到另一个列表。

            【讨论】:

              【解决方案10】:

              Lazarus:
              我认为编译器会意识到我想要对列表的对象执行操作,而不是我试图强制转换列表本身。

              更多信息:

              public abstract class ObjBase
                 {
                 }
              
              internal interface IDatabaseObject
                 {
                 }
              
              public class Obj : ObjBase, IDatabaseObject
                 {
                 }
              
              
              internal interface IDatabaseObjectManager
                 {
                    List<ObjBase> getSomeStuff();
                 }
              
              public class ObjManager : IObjManager
              {
                  public List<Obj> returnStuff()
                  {
                     return getSomeStuff().Cast <Customer>().ToList<Customer>();
                  }
              
                  private List<ObjBase> getSomeStuff()
                  {
                     return new List<ObjBase>();
                  }
              }
              

              现在,该 DLL 之外的客户端代码可以执行: ObjManager objM = 新的 ObjManager(); 列表 listOB = objM.returnStuff(); 我将为应用程序的这一部分 (O/RM) 创建几个 ObjObjManager 类型。

              (该死的评论块用完了字符!:-)

              【讨论】:

                【解决方案11】:

                这是我修复转换的方法

                list<SomeOtherObject>
                

                到一个

                object
                

                然后到一个

                List<object>
                

                https://stackoverflow.com/a/16147909/2307326

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-01-19
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多