2010-07-28 26 views
2

我有类似以下的类:使用泛型 - 从通用类中调用通用类

public abstract class Manager<T, TInterface> : IManager<T> where TInterface : IRepository<T> 
{ 
    protected abstract TInterface Repository { get; } 

    public virtual List<T> GetAll() 
    { 
     return Repository.GetAll(); 
    } 
} 

这工作完全正常,但是,有没有办法从具有TInterface中脱身在产生的类,它扩展我的一般抽象类抽象类的声明和:

public class TestManager : Manager<TestObject, ITestRepository>, ITestManager 

我被迫使用ITestRepository,使存储库属性的抽象,由于事实,即它可以包含我需要知道自定义方法关于并能够打电话。

随着我继续构建图层,我将不得不继续完成整个过程。例子是,如果我有一个通用的抽象控制器或服务层:

public class TestService : Service<TestObject, ITestManager>, ITestService 

是否有更好的方法来做到这一点或这是允许一个泛型类调用另一个泛型类的最佳做法?

+0

会使用Action <>或Func <>帮助缓解您的问题吗?这取决于何时/为什么要调用这些函数。 – 2010-07-28 13:58:02

+0

我对你为什么在这个例子中使用一个通用的存储库的情况有点困惑。如果它每次都要实施IRepository ,为什么你要使用泛型。在你展示的代码中,没有理由需要通用的TInterface。你能否解释一下你在这种情况下使用泛型的理由,因为它可能是出于你没有显示的原因,但你可能能够消除它,从而避免你的问题 – 2010-07-28 14:05:48

+0

@Craig Suchanec:原因是存储库类必须有一个方法GetAll(),返回一个T列表。这意味着存储库类在T上被参数化。 – siride 2010-07-28 14:09:16

回答

1

似乎所有你想要做的就是让Manager<T>可测试的,并使用一个模拟的,你可以查询特殊成员库。

如果是这样的话,也许你可以在你的设计改成这样:

public class Manager<T> : IManager<T> { 
    protected IRepository<T> Repository { get; set; } 
    // ... 
    public virtual List<T> GetAll() { 
    return Repository.GetAll(); 
    } 
} 

现在,测试的所有细节都在测试子类:

public class TestingManager<T> : Manager<T> { 
    public new ITestRepository<T> Repository { 
    get { 
     return (ITestRepository<T>)base.Repository; 
    } 
    set { 
     base.Repository = value; 
    } 
    } 
} 

当你写你的单位测试,您创建TestingManager<T>实例(通过TestingManager<T>声明的变量和字段引用),并为您提供测试存储库。每当你查询他们的Repository,你总会得到一个强类型的测试库。

UPDATE:

还有另一种方式来解决这个问题,没有一个子类。您将存储库对象声明为测试存储库,并将其传递给Manager<T>,并直接查询它们,而不通过Manager<T>

[Test] 
public void GetAll_Should_Call_GetAll_On_Repository_Test() { 
    var testRepository = new TestRepository(); 
    var orderManager = new Manager<Order>(testRepository); 
    // test an orderManager method 
    orderManager.GetAll(); 
    // use testRepository to verify (sense) that the orderManager method worked 
    Assert.IsTrue(testRepository.GetAllCalled); 
} 
+0

我改变了我的代码,看起来像你提供和运行我的单元测试,他们失败了。经理类方法正在查看Repository属性,该属性返回一个IRepository 实例。由于没有设置该属性,而是设置了“公共新ITestRepository 存储库”属性,因此在管理器抽象类中存储库属性获取NullReferenceException。 – Brandon 2010-07-28 14:59:58

+0

对不起,我忘了在子类属性上使用'base'。但是不要把我的代码作为整个实现,就像一个想法一样。 – 2010-07-28 15:05:12

+0

将库添加到Repository调用使所有工作都完美无缺。这是一个理想的解决方案,但是,由于我必须从管理器的Repository属性中删除摘要修饰符,因此不会在TestManager 级别强制实施。这很好,因为如果不存在自定义方法,则不需要实现它。 – Brandon 2010-07-28 15:16:00

0

不,你无法绕过它。你可以尝试,但结果会很糟糕,并且在某些方面不正确。原因是你要求泛型不是通用的,但仍然是通用的。

如果一个新类使用泛型类,无论是继承还是组合,并且它本身并不知道足以为它正在使用的泛型类指定类型参数,那么它本身就是通用的。它类似于方法调用链,其中一个方法可以将参数传递给另一个方法。它不能构成内部方法的参数,但必须将它们作为参数本身从一个确实知道它们的调用者那里获取。类型参数是相同的。

有一件事确实使这种感觉像代码气味是事实,你不能有一个类型的变量经理<,>。它必须是完全类型指定的。我提出的一个解决方案是具有泛型类实现的非泛型接口。这些接口具有尽可能多的泛型类的公共接口(它们不能具有引用类型参数的方法或属性)。然后你可以传递接口类型的变量,而不必指定类型参数。

例子:

interface IExample { 
    string Name { get; } 
    void SomeNonGenericMethod(int i); 
} 

class Example<T> : IExample { 
    public string Name { get { ... } } 

    public void SomeNonGenericMethod(int i) { 
     ... 
    } 

    public T SomeGenericMethod() { 
     ... 
    } 
} 
+0

我有一种感觉,我会卡在这个位置。我希望能够以某种方式将抽象Repository属性的实现“扩展”为我需要的类型。我想我仍然可以在需要调用自定义方法时将其强制键入“ITestRepository”,但这与我目前实施的方式差不多。 – Brandon 2010-07-28 14:21:23