【问题标题】:Cast Parent class to Child将父类转换为子类
【发布时间】:2014-04-06 18:01:00
【问题描述】:

我们有 6 个 ObservableCollection List<Parent> 中的 ObservableCollection<T>,它们都有不同类型的子类。

我们想要做的是使用一个通用方法来检索所有具有相同类型的对象,换句话说,检索一个包含所有<T> 孩子的列表。

这是我的源代码

类 A 和 B 是 Parent 的子类。

ObservableCollection<ManagerTemplate> ManagerListStack = new ObservableCollection<ManagerTemplate>(ManagerTemplates);


class ManagerTemplate
{
 public Type _Type { get; set; }
 public ObservableCollection<Parents> parentList {get;set;}
}

internal static List<ManagerTemplate> ManagerTemplates = new List<ManagerTemplate>()
{
    new ManagerTemplate{ List= new ObservableCollection<Parent>(),Type=typeof(A)},
    new ManagerTemplate{ List= new ObservableCollection<Parent>(),Type=typeof(B)}
};

public static List<T> Get<T>() where T : Parent
{
    /*(ManagerListStack.Where(x => x._Type == typeof(T)).First().List.Cast<T>()).ToList(); -- TRY 1*/
    /*(List<T>)(ManagerListStack.Where(x => x._Type == typeof(T)).First().List.Cast<T>())* -- TRY 2*/
    return  (ManagerListStack.Where(x => x._Type == typeof(T)).First().List.Cast<T>()).ToList();
}

使用

(ManagerListStack.Where(x => x._Type == typeof(T)).First().List  as List<T>)

返回的列表不包含任何元素。我100%确定并且已经调试了列表,里面有元素。

使用

(List<T>)(ManagerListStack.Where(x => x._Type == typeof(T)).First().List.Cast<T>())

我收到错误消息“无法从父级转换为 A 或 B”

【问题讨论】:

  • 我是否正确理解 ManagerTemplate 将始终持有单一类型 Parent 的单一 ObeservableCollection?
  • 是的,正确的。每个 ManagerTemplate 内部都会有一个 ObserableCollection,其中包含相同类型的对象。
  • 你的问题是什么?您似乎已经有了解决方案。
  • 解决方案不起作用。
  • SelectMany to flatten 而不是OfType to select 可能会产生更易于阅读的代码...请注意,“解决方案不起作用”是对问题的错误解释 - 您应该先调试它看看究竟是什么不起作用 - 试图在一个语句中进行复杂的过滤并不是调查 LINQ 问题的最简单方法......

标签: c# list generics casting


【解决方案1】:

SomeListOfX as List&lt;Y&gt; 永远不会工作。 Y 派生自 X 的事实并不意味着 List&lt;Y&gt; 派生自 List&lt;X&gt;!这两种列表类型不兼容;它们只是两种不同的类型。

List&lt;Parent&gt; 不能转换为 List&lt;Child&gt;,即使它只包含 Child 类型的项,因为 C# 在编译时只知道静态类型,而不知道运行时类型。该列表可能包含不属于 Child 类型的项目。

顺便说一句,相反的也行不通。因为如果您能够将List&lt;Child&gt; 转换为List&lt;Parent&gt;,那么您可以将ParentAnotherChild 类型的项目添加到List&lt;Parent&gt;,但由于基础列表仍然是List&lt;Child&gt; 类型这会搞砸的!请注意,转换对象不会创建新对象(即它不会转换对象),它只是告诉 C# 将其视为另一种类型。例如。如果你知道parent 引用了一个孩子,你可以说Child child = (Child)parent;


(List<T>)(ManagerListStack.Where(x => x._Type == typeof(T)).First().List.Cast<T>())

Cast&lt;T&gt; 产生一个IEnumerable&lt;T&gt;,你不能将IEnumerable&lt;T&gt; 转换为List&lt;T&gt;!可枚举不是列表。


什么是有效的

List<Y> listOfY = listOfX.Cast<Y>().ToList();

如果X 可以转换为Y


Get&lt;T&gt; 中的第三个(未注释)示例有效:

return ManagerListStack
    .Where(x => x._Type == typeof(T))
    .First().List
    .Cast<T>()
    .ToList();

【讨论】:

    【解决方案2】:

    首先,就像 Olivier 上面所说的,List&lt;Child&gt; 不是 List&lt;Parent&gt; 的子类,即使 ChildParent 的子类。 ObservableCollection 也是如此。这个主题被称为方差。在这里深入讨论它太过分了,但您可以查看C# : Is Variance (Covariance / Contravariance) another word for Polymorphism? 了解更多详细信息。 IEnumerable&lt;T&gt; 是协变的。我要切换到那个。如果您特别需要列表或可观察集合,您可以在之后简单地构造它们,甚至将它们输入。

    你想要的是一个强类型的 Get 函数,它从 ObservableCollection 中返回一个任意的 IEnumerable>,其中 U 是 T 的子节点。让我们看看我们是否可以写出这个签名:

    IEnumerable&lt;U&gt; Get&lt;U, T&gt;(ObservableCollection&lt;IEnumerable&lt;T&gt;&gt; source) where U : T

    这是我们想要的最抽象的版本,但我们实际上并不需要它是通用的:我们在编译时已经知道基本类型。为了便于阅读,我们可以将其更具体:

    IEnumerable&lt;U&gt; Get&lt;U&gt;(ObservableCollection&lt;IEnumerable&lt;Parent&gt;&gt; source) where U : Parent

    现在让我们填写该函数的主体:

    IEnumerable<U> Get<U>(ObservableCollection<IEnumerable<Parent>> source) where U : Parent {
      return source.First(inner => inner is IEnumerable<U>)
    }
    

    哇,这其实很简单!

    这并不是你真正想要的 api。如果我们把所有东西放在一起,我们会得到

    ObservableCollection<IEnumerable<Parent>> ManagerListStack = new ObservableCollection<IEnumerable<Parent>>(ManagerTemplates);
    
    
    internal static List<IEnumerable<Parent>> ManagerTemplates = new List<IEnumerable<Parent>>
    {
        new IEnumerable<ChildA>(){},
        new IEnumerable<ChildB>(){},
    };
    
    IEnumerable<U> Get<U>() where U : Parent {
      return ManagerListStack.First(inner => inner is IEnumerable<U>)
    }
    

    现在,我们仍然没有您想要的行为:ObservableCollections of ObservableCollections。事实证明,我们无法在 C# 的类型系统中正确执行此操作。然而,我们可以做的是将责任从类型系统转移到程序员身上。我们保留ObservableCollection&lt;IEnumerable&lt;Parent&gt;&gt;,但我们用ObservableCollections&lt;Child&gt; 填充它。这是可能的,因为ObservableCollection&lt;Child&gt; 实现了IEnumerable&lt;Child&gt;,而IEnumerable&lt;Child&gt;IEnumerable&lt;Parent&gt; 的子类型。

    ObservableCollection<IEnumerable<Parent>> ManagerListStack = new ObservableCollection<IEnumerable<Parent>>(ManagerTemplates);
    
    
    internal static List<IEnumerable<Parent>> ManagerTemplates = new List<IEnumerable<Parent>>
    {
        new ObservableCollection<ChildA>(){},
        new ObservableCollection<ChildB>(){},
    };
    
    ObservableCollection<U> Get<U>() where U : Parent {
      return (ObservableCollection<U>)ManagerListStack.First(inner => inner is ObservableCollection<U>)
    }
    

    我们使用演员表作为一个逃生舱口来绕过不提供协变 ObservableCollection 的库的限制(无论如何这似乎是不可能的,所以不要为此责备他们)

    我在 LinqPad 中用来测试的完整程序:

    void Main()
    {
      new Test().Get<ChildA>().First().Talk();
    }
    
    class Test {
    
            internal List<IEnumerable<Parent>> ManagerTemplates;
            private ObservableCollection<IEnumerable<Parent>> ManagerListStack;
    
            public Test() {
            this.ManagerTemplates = new List<IEnumerable<Parent>>
            {
                new ObservableCollection<ChildA>(){ new ChildA()},
                new ObservableCollection<ChildB>(){ new ChildB()},
            };
    
             this.ManagerListStack = new ObservableCollection<IEnumerable<Parent>>(this.ManagerTemplates);
             }
    
            public ObservableCollection<U> Get<U>() where U : Parent {
              return (ObservableCollection<U>)ManagerListStack.FirstOrDefault(inner => inner is ObservableCollection<U>);
            }
    }
    
    abstract class Parent {
        abstract public void Talk();
    }
    
    class ChildA : Parent {
        public override void Talk(){
          Console.WriteLine("I'm an A");
        }
    
    }
    
    class ChildB : Parent {
    public override void Talk(){
          Console.WriteLine("I'm a B");
        }
    }
    

    正如预期的那样,它会打印“我是 A”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-18
      • 1970-01-01
      • 2020-03-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多