通用基础 class 构造函数

Generic base class constructor

使用这个通用基础 class:

public abstract class Logic<U> where U : class
{
    protected U m_provider;
    public Logic(U provider)
    {
        m_provider = provider;
    }
}

我正在尝试为单元测试创​​建基础测试class:

public class LogicBaseTest<T, U> where T : Logic <U>, new()  where U: class
{
    protected T m_logic;
    protected U m_provider;

    [OneTimeSetUp]
    public virtual void OneTimeSetup()
    {
        m_provider = (U)Substitute.For<IInterface>();

        m_logic = new T(m_provider);
    }
}

它抱怨构造函数,它请求 new() 约束,但是当我添加它时它抱怨构造函数不能接受参数。

我可以添加一个方法来填充提供程序,但我想知道它是否可以在构造函数中完成。

所以你这里有两个问题:

  1. LogicBaseTest 需要知道如何实例化一个 Logic<U>
  2. Logic<U> 在构造函数中需要 U

我建议的解决方案是将工厂委托传递给基础测试 class 并删除 new() 要求。然后您的设置可以使用工厂构造 Logic class:

public class LogicBaseTest<T, U> 
    where T : Logic<U>
    where U: class
{

    protected readonly Func<U, T> _factory;

    public LogicBaseTest(Func<U, T> factory)
    {
        _factory = factory;
    }

    [OneTimeSetUp]
    public virtual void OneTimeSetup()
    {
        m_provider = (U)Substitute.For<IInterface>();
        m_logic = _factory(m_provider);
    }
}

在派生测试中 class 你只需要告诉基础 class 如何新建一个 Logic<U>:

public class DerivedTest : LogicBaseTest<Logic<MyUType>, MyUType>
{
    public DerivedTest()
        : this(u => new Logic<MyUType>(u))
    {
    }
}

您不能添加泛型类型约束,例如 where T : new(U)。 相反,您可以使用工厂。

public interface IFactory<out TObject, in TProvider>
{
    public TObject Create(TProvider provider);
}

然后在你的基础测试中使用它

public class LogicBaseTest<T, U> where T : Logic <U> where U: class // remove new()
{
    // fields

    private readonly IFactory<T, U> _factory;

    public LogicBaseTest(IFactory<T, U> factory)
    {
        _factory = factory;
    }

    [OneTimeSetUp]
    public virtual void OneTimeSetup()
    {
        m_provider = (U)Substitute.For<IInterface>();
       
        m_logic = _factory.Create(m_provider);
    }
}

例子

public class Logic1Provider
{
    
}

public class Logic1Factory : IFactory<Logic1, Logic1Provider>
{
    public Logic1 Create(Logic1Provider provider)
    {
        return new Logic1(provider);
    }
}

public class Logic1 : Logic<Logic1Provider>
{
    public Logic1(Logic1Provider provider) : base(provider)
    {
    }

    public void DoDomeLogic()
    {
        // do stuff
    }
}
var factory = new Logic1Factory();
var baseTest = new LogicBaseTest<Logic1, Logic1Provider>(factory);

让我们将您的通用约束分解为简单的英语

where T : Logic<U>, new()

这意味着

The type of T needs to inherit from Logic, the generic type parameter must be U and have a public, parameterless, constructor

但问题是 Logic 本身已经打破了这个限制。现在,我们如何解决这个问题?有多种方式

  1. 使用“工厂函数”实例化您的 m_logic 并移除 new 约束(参见

  2. 删除 new() 约束并使用 Activator.CreateInstance 之类的东西来实例化您的 Logic class

  3. 将无参数构造函数添加到 Logic 并以其他方式配置您的 m_provider

  4. m_logic 的实例化移动到单元测试本身(如果您需要为大量单元测试创​​建相同的 Logic,可以添加一个辅助方法)

  5. 研究你的单元测试框架是否支持某种形式的依赖注入并注入你需要的一切