【问题标题】:Cast a concrete class instance to an interface that uses generics将具体类实例强制转换为使用泛型的接口
【发布时间】:2011-05-31 22:20:25
【问题描述】:

这是我从抽象基类派生的具体存储库

public class DepartmentRepository : RepositoryBase<Department>, IDepartmentRepository

public abstract class RepositoryBase<T> : IRepository<T> where T : class, IPersistantBusinessObject

这是我的界面

public interface IDepartmentRepository : IRepository<Department>

public interface IRepository<T> where T : IPersistantBusinessObject

public interface IPersistantBusinessObject { ... }

这是我的实体

public class Department : BusinessObjectBase

public abstract class BusinessObjectBase : IPersistantBusinessObject

如何将我的 DepartmentRepository 转换为通用 IRepository?

DepartmentRepository rep = new DepartmentRepository();
(IRepository<IPersistentBusinessObject>)rep; // this doesn't work

想法?如果您想知道,这是一个使用存储库模式的 asp.net MVC3 应用程序。我是否过度设计了接口?我现在应该如何使用通用存储库?

谢谢。

编辑: 这是我的 IRepository 界面。当被问及为什么我需要强制转换为 IRepository 时,实际上只是为了我可以使用“SaveChanges”方法,因为我想拦截数据库中的任何异常并以通用方式处理。

public interface IRepository<T> where T: IPersistantBusinessObject
{
    IQueryable<T> Retrieve();
    T Retrieve(int id);
    IQueryable<T> RetrieveActiveAndInActive();
    T RetrieveActiveAndInActive(int id);
    void Add(T domainObject);
    void SaveChanges();
    void Delete(T domainObject);
    void Delete(int id)
 }

【问题讨论】:

  • “这不起作用”是什么意思? 确切的错误信息是什么?
  • 回答“我是否过度设计了接口”:是的。我不确定你从这个奇怪的继承/实现图中获得了什么。另外,如果您已经将其作为DepartmentRepository,为什么要直接将其转换为该接口?
  • @Jim - 我得到一个 InvalidCastException。

标签: c# generics interface casting


【解决方案1】:

你想在这里做什么?我想不出你想投给IRepository&lt;IPersistentBusinessObject&gt;的情况。什么代码依赖什么抽象?

我通常希望其他代码依赖于IDepartmentRepository 的抽象,并且根本看不到IRepository&lt;T&gt; 甚至IPersistentBusinessObject 的用例。同样,如果您可以找到依赖于该抽象的代码用例,那么它们就很有价值;否则,YAGNI

(顺便说一句,这是测试驱动开发有价值的原因之一。您可以通过编写单元测试来确定需要哪些抽象,并查看需要模拟哪些类来隔离某些功能的测试,而不是只是模糊地将所有内容抽象为接口,因为您被告知它们比具体类型更好。从具体类型开始,当您的测试迫使您提取接口时,这是正确的时间!)


真的,这是covariance of generic types 的问题。如果你说它应该可以工作

public interface IRepository<out T> where T : IPersistantBusinessObject

【讨论】:

  • 我得试试上面的方法,再多考虑一下。我的目标是为我的存储库和业务对象使用接口,并且我还希望它们具有通用基类……这意味着我的接口使用了泛型。
  • 哦,常见的用法其实是在我的单元测试用例中。例如我在 nunit 中的存储库: Assert.Throws(rep.Save())... 然后是一大堆测试。我想将所有这些都包含在一个共同的例程中。
  • 对我来说,抽象泛型基类作为实现细节是有意义的,但作为契约的泛型接口却没有。
【解决方案2】:

您需要将其转换为IRepository&lt;Department&gt;

DepartmentRepository 派生自实现 IRepository&lt;Department&gt;RepositoryBase&lt;Department&gt;

你可能想看看co and contravariance...

【讨论】:

  • 感谢您的链接,但我不想将其转换为 IRepository...我还有其他想要以“通用”方式使用的具体存储库...例如想想产品、订单、类别等。
【解决方案3】:

正如 Daniel 所暗示的,您不能将其强制转换为 IRepository,因为您已将 IRepository 接口定义为仅与另一种类型 T 一起存在。因此“IRepository”作为接口不存在。

如果您希望能够使用接口“IRepository”,那么它需要是一个独立的接口,不依赖于类型。

也许您需要两个接口,一个实现您在 IRepository 中所需的基本功能,另一个实现类型 T 的泛型方法。 例如

<!-- language: lang-c# -->
public interface IRepository
{
  SaveChanges()
...
}

public interface IRepositoryFor<T> where T : IPersistantBusinessObject
{
  T GetFirst(); // or whatever
...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-08
    相关资源
    最近更新 更多