【问题标题】:Downcasting baseclass into derived generic type C#将基类向下转换为派生的泛型 C#
【发布时间】:2020-09-09 15:54:32
【问题描述】:

我正在努力解决与泛型之间转换相关的问题,这可能是一个简单的问题。 基本上我想创建一个基类列表并添加多个类。

public interface IQueryEngineDependency
{
    public IEnumerable<QueryDependencyDetail> GetDependencies<>();
}

public abstract class QueryDependencyDetail
{
    public int Order { get; set; }
}

public class QueryDependencyDetail<TEntity, TKey> : QueryDependencyDetail
        where TEntity : BaseEntity<TKey>
{
    public virtual Func<TEntity, object> Key { get; set; }

    public IQueryable<TEntity> Data { get; set; }

    public Func<TEntity, object> JoinKey { get; set; }

    public Expression<Func<TEntity, bool>> WhereClause { get; set; }   
}

问题

例如,我有一个类实现了上面显示的接口,但我正在找出实现它的正确方法。

public class TestQueryDependency : IQueryEngineDependency
{
        public IEnumerable<QueryDependencyDetail> GetDependencies()
        {
            var dependencies = new List<QueryDependencyDetail>
            {
                new QueryDependencyDetail<Tasks, long>
                {
                    Order = 1,
                    Data = null // just to simplify
                }
            };

            return dependencies;
        }
}

如果我在代码中的某处调用GetDependencies 方法,我该如何进行向下转换以访问泛型类型字段?我的意思是我会得到QueryDependencyDetail 类型的实例。那么是否可以将其转换为QueryDependencyDetail&lt;TEntity, TKey&gt;? 还是有其他方法可以做到这一点?

编辑

var testDep = new TestQueryDependency();
var dependencies = testDep.GetDependencies();

请记住,在我的特定实现中,依赖项可能有多达 20 个不同的实例。 例如,我如何访问数据字段? (只是一个简单的场景)

var first = dependencies.FirstOrDefault()?.Data; ?????

我将需要它来使用 LINQ 执行动态查询。

提前致谢。

【问题讨论】:

  • 我的代码没有任何问题。你说的向下投射是什么意思。能否请您详细说明问题
  • 我将编辑问题以使其更加明确。 Tks
  • 如果调用 GetDependencies 的人需要 QueryDependencyDetail&lt;TEntity, TKey&gt;,而不是 QueryDependencyDetail,那么 这就是该方法需要返回的内容。否则它可能根本就不是QueryDependencyDetail&lt;TEntity, TKey&gt; 实例,您将无法更改它。
  • IQueryableIQueryable&lt;T&gt;的关系类似。 IQueryable 有一个 ElementType 属性,IQueryable&lt;T&gt; 实现返回 typeof(T)。如果GetDependencies 可以返回多个不同紧密构造类型的QueryDependencyDetail&lt;TEntity, TKey&gt; 对象,那么在QueryDependencyDetail 中具有EntityTypeKeyType 属性可能使您能够完成所有需要的反射和表达式构建。但是,如果不知道如何您打算使用它“使用 LINQ 执行动态查询”,很难更具体。
  • edit 您的问题包括如何您打算使用返回的序列(如问题所示,而不是 cmets 中的变化)。如果您不知道需要什么来构建查询表达式,则无法知道如何更改它。

标签: c# .net linq generics


【解决方案1】:

我不确定你想要完成什么。

如果将泛型参数放在引擎接口上会怎样?

public interface IQueryEngineDependency<TEntity,TKey> where TEntity : BaseEntity<TKey> {
    public IEnumerable<QueryDependencyDetail<TEntity,TKey>> GetDependencies();
}

然后你可以创建一个测试引擎:

public class TestQueryDependency : IQueryEngineDependency<Tasks,long> {
    public IEnumerable<QueryDependencyDetail<Tasks,long >> GetDependencies() {
        var dependencies = new[] {
                new QueryDependencyDetail<Tasks, long> {
                    Order = 1,
                    Data = null // just to simplify
                }
            };

        return dependencies;
    }
}

【讨论】:

  • 它应该具有任务或任何其他域对象依赖项的列表。我不能只限制任务的返回对象。唯一的疑问是当你得到基类的列表时,比方说,在应用程序启动中。每个示例如何访问数据字段?此时只能访问订单字段。
【解决方案2】:

你应该可以直接投射它,即(QueryDependencyDetail&lt;TEntity, TKey&gt;) myObject

但是,您必须确保类型实际上与真实对象匹配。让我们举一个简化的例子:

    public class A { }
    public class B<T> : A { }
    public static B<T> Test<T>(A a) => (B<T>)a;

    var a = new B<int>();
    var b1 = Test<int>(a); // works since a is of type of B<int>
    var b2 = Test<string>(a); // Will throw invalid cast exception since a is not of type B<string>

你也可以测试类型:

if(a is B<int> b)

这里的问题是你必须知道对象的实际类型。你不能只是将a 转换为B&lt;T&gt; 而不在某个地方声明T 实际上是什么。

我用来解决这类问题的解决方案是避免任何需要知道泛型类型的事情。确保接口或基类包含与对象交互时所需的所有方法。当涉及多个类时,这可能会有点复杂,但通常是可以的。

编辑: 第三种选择可能是使用反射。这可以让您检查泛型类型参数的实际类型。它可能允许使用相同的泛型类型参数创建另一个对象。缺点是使用反射可能非常麻烦,并且可能容易出错且速度慢。

【讨论】:

  • 首先感谢您的回答。当您调用您不知道或可能没有 TEntity 或 TKey 的方法来进行您建议的显式转换时。我可以用多个 if ( a is B b) 来检查它。但这是我试图避免的方法
  • 让我们从多个if 语句开始。他们每个人会是什么样子?不仅是测试,还有你会为你测试的每种类型做些什么。将其添加到问题中。这将是不需要了解特定类型和 if 语句来单独处理它们的答案的基础。
  • @madreflection tks 你坚持要帮我解决这个问题。基本上,在那之后我想构建一个能够动态加入多个 IQueryable 的引擎。就像我在描述中提到的那样,我被卡住了,因为我需要访问泛型类型实现中的字段。我不确定一旦通过这个,我是否能够像我最初想的那样处理连接。
  • 这一切都很好,但是你没有提供任何细节,只提供高级描述,而且你没有做你需要做的事情,即 将它添加到问题。我相信我可以帮助你,但我不会猜测。如果你知道在返回的序列中有一个QueryDependencyDetail&lt;Tasks, long&gt;,你会用它做什么?显示回答该问题的代码
  • @TiagoMourão,也许我应该更清楚我的问题:你会用它做什么“使用 LINQ 执行查询”(忘记动态)?我同意 JonasH:如果您只想访问 Data 属性,则不需要它是通用的。我问如果你知道你有一个QueryDependencyDetail&lt;Tasks, long&gt;,你会怎么做。如果您知道您正在处理特定的、紧密构造的类型,您将如何使用其属性构建查询?如果您的示例未使用任何属性,请将它们从类型中删除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-12
  • 1970-01-01
  • 1970-01-01
  • 2016-11-15
  • 1970-01-01
相关资源
最近更新 更多