将Parent类转换为Child

时间:2014-04-06 18:01:00

标签: c# list generics casting

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

我们想要做的是使用通用方法来检索具有相同类型的所有对象,换句话说,检索包含所有<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>())

我收到错误&#39;无法从家长投射到A或B&#39;

2 个答案:

答案 0 :(得分:1)

SomeListOfX as List<Y>永远不会奏效。 Y派生自X这一事实并不意味着List<Y>来自List<X>!这两种列表类型不兼容;它们只是两种不同的类型。

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

顺便说一句,相反的情况也不起作用。因为如果您能够将List<Child>投射到List<Parent>,那么您可以向Parent添加AnotherChildList<Parent>类型的项目,但是基础列表仍然是List<Child>类型,这将是f *** up!注意,转换对象不会创建新对象(即它不会转换对象),它只是告诉C#将其视为另一种类型。例如。如果你知道Child child = (Child)parent;引用了一个孩子,你可以说parent


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

Cast<T>会产生IEnumerable<T>,您无法将IEnumerable<T>投射到List<T>!可枚举的不是列表。


什么有效

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

如果X可以投放到Y


Get<T>中的第三个(未注明的)示例有效:

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

答案 1 :(得分:1)

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

你想要的是一个强类型的Get函数,它从ObservableCollection&gt;返回任意IEnumerable。其中U是T的孩子。让我们看看我们是否可以写下该签名:

IEnumerable<U> Get<U, T>(ObservableCollection<IEnumerable<T>> source) where U : T

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

IEnumerable<U> Get<U>(ObservableCollection<IEnumerable<Parent>> 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 ObservableCollections。事实证明,我们无法在C#的类型系统中正确地做到这一点。然而,我们可以做的是将责任从类型系统转移到程序员。我们保留ObservableCollection<IEnumerable<Parent>>,但我们会将其填入ObservableCollections<Child>。这是可能的,因为ObservableCollection<Child> imlements IEnumerable<Child>IEnumerable<Child>IEnumerable<Parent>的子类型。

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”