【问题标题】:Converting List of base to derived class将基类列表转换为派生类
【发布时间】:2013-11-28 00:49:50
【问题描述】:

我有一个基类

 public Enum TestType{
   A,
   B
 }

public abstract class Base{
    public int ID{ get;set;}
}

还有一个派生类

public class Derived1 : Base{
   public string Prop1 {get;set;}
}
public class Derived2 : Base{
   public string Prop2 {get;set;}
}

然后我有一个返回 Base 的函数

public IQueryable<Base> GetData(TestType type){
   switch(type){
      case A:
        return new IQueryable<Derived1>();
      case B:
        return new IQueryable<Derived2>();
    }

}

遗憾的是,我正在使用的其中一个工具需要实际类型,并且无法通过传递基本类型。

如何将 GetData 的返回结果转换为它的实际类型而不是 Base。我正在考虑使用表达式和演员,但我无法弄清楚。

理想情况下,GetData 是一个获取数据的工厂,我不希望 IQueryable 包含混合类列表。我不知道编译时的类型!我想在运行时有一个从基础到派生的函数,而不必检查底层类型然后进行强制转换(即一堆 if 语句)

编辑:添加了一些底层代码来提供帮助。现在我正在使用的控件说它在 base 上找不到属性 Prop1(如果我传入返回 IQueryable 的 GetData 结果集),但如果我转换它,它工作正常。问题是我在编译时不知道确切的类型我只知道其中一个类会在那里。

【问题讨论】:

  • 我不认为你可以在不实际施放的情况下逃脱。您希望如何拥有一个包含混合项目的列表?
  • 不会有混合项目
  • @dbarnes 能否请您在IQueryable&lt;Base&gt; GetData() 中输入一些代码来帮助演示如何返回不同的数据类型?如果您不想要一堆 if 语句,我们需要查看数据如何生成的逻辑,因为类似的逻辑需要进入强制转换。
  • 好吧,我又添加了一些代码

标签: c# oop


【解决方案1】:

LINQ 有一个 Cast&lt;&gt; 调用,它应该适合你,正如 here 所记录的那样。

var myList = GetData().Cast&lt;Derived1&gt;();

或者,您甚至可以使用Select 扩展方法,如下所示:

var myList = GetData.Select(data =&gt; data as Derived1);

编辑:根据您最近的问题编辑,我建议您实施Factory pattern。创建一个工厂,它将完全基于传入的枚举值返回一个特定的类对象。这些不同的类对象都应该实现一个特定的接口,该接口上具有GetData() 方法,该方法返回该特定派生的IQueryable类型。您需要为您的工具添加的任何逻辑也可以在该类对象上完成(并且也可以放在接口上,以便您的原始代码也可以调用它)。

这样做可以让您的代码在运行时动态确定使用哪一个,还可以让您遵循开闭原则。如果您添加了更多派生类型,您需要做的就是创建一个实现接口的新类对象,确保工厂知道它存在,然后让它运行。

【讨论】:

  • 任何数字,但现在我有 4 个,每个 IQueryable 具有相同的类型,我正在考虑执行 OfType 并检查 null 并继续,但我不认为那是最佳选择。
  • @dbarnes 如果您在编译时不知道该类型,那么您将如何将其静态键入到变量中?
  • 我在编译时就知道了,但它返回 IQueryable,我需要将它转换成它的底层类型。
  • 第二个使用Select 的例子类似于内置的OfType&lt;T&gt; 扩展:var myList = GetData.OfType&lt;Derived1&gt;(),内置方法将过滤掉不属于指定类型的对象 - 不确定是否这是我们想要的。
  • +1。只是提到存在替代方案;使用 "ConvertAll" 而不是 "Select" (stackoverflow.com/questions/1571819/…)
【解决方案2】:

使用泛型并使您的 GetData 方法泛型:

public IQueryable<T> GetData<T>() where T : Base
{
    var data = ...; //object to return
    return (IQueryable<T>)data;
}

我认为这最终会满足您的要求。

【讨论】:

  • 编译时不知道类型:/
  • 编译时不知道哪种类型?您要转换成的类型?
  • 我想从 Base 转到它的底层派生,我正在研究创建一个表达式,使用对底层类型的反射来实现这一点,我只是认为可能有更好的方法。
  • 好的,但是至少当你想使用它时你必须知道类型(具体派生类型)并且在那里你可以使用泛型?
【解决方案3】:

我最终做了以下事情:

var ds = GetData(...);
if(ds.Any()){
    var m = typeof(Queryable).GetMethod("Cast",BindingFlags.Static | BindingFlags.Public, null,new[] { typeof(IQueryable<Base>) }, null).MakeGenericMethod(ds.First().GetType());
    var r = m.Invoke(ds, new[] {ds});
}

任何人有问题或在这里看到问题?

【讨论】:

  • 在这种情况下,您的可读性不好,更不用说维护这将非常痛苦,特别是如果除了您之外的其他人最终不得不这样做......
  • @Freerider 我在编译时不知道类型,所以这是唯一的途径,如果你知道类型,那么一定要使用其他答案。
猜你喜欢
  • 2022-10-14
  • 2017-01-08
  • 2013-11-17
  • 1970-01-01
  • 1970-01-01
  • 2016-02-26
  • 2021-02-18
相关资源
最近更新 更多