Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
184 views
in Technique[技术] by (71.8m points)

How to pass a class constructor as parameter in C#

I want to create generic Unit Test classes for Services. My base class is as generic as possible and I wanna pass the Service constructor as a parameter to my base class from the derived class, but I don't no how to do it in C#.

public interface IBaseServiceUnitTest<TEntity> where TEntity : BaseEntity
{
    //...some methods
}

public class BaseServiceUnitTest<TEntity> : IBaseServiceUnitTest<TEntity> where TEntity : BaseEntity
{
    private IBaseService<TEntity> _service;

    public BaseServiceUnitTest(Constructor ctor)
    {
        _service = ctor();
    }

    //...implemented methods from IBaseServiceUnitTest
}

public class CustomEntityServiceUnitTest : BaseServiceUnitTest<CustomEntity>
{
    public CustomEntityServiceUnitTest() 
        : base(Constructor ctor)
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I think a factory method would serve you well here.

Mark your base test fixture as abstract and define a protected abstract method with the same signature as the service constructors. Then, call that method in your base fixture's setup logic.

You will have to implement the abstract method for each derived test class, but I think it's a reasonable compromise.

Note that I also removed the interface from the test class. Based on the sample code you provided, it's entirely redundant with the now-abstract base class. Of course, it might be useful for some reason not apparent from your example--if so, you can keep it.

public class BaseServiceUnitTest<TEntity> where TEntity : BaseEntity
{
    private IBaseService<TEntity> _service;

    public BaseServiceUnitTest(Constructor ctor)
    {
        _service = Create(/* your mocked dependencies here */);
    }

    protected abstract IBaseServce<TEntity> Create(Dependency1 d1, Dependency2 d2);

    //...implemented methods from IBaseServiceUnitTest
}

public class CustomEntityServiceUnitTest : BaseServiceUnitTest<CustomEntity>
{
    // "arg1, arg2"
    protected override IBaseService<CustomEntity> Create(Dependency1 d1, Dependency2 d2) =>
        new BaseService<CustomerEntity>(d1, d2);
}

Addendum

If you really want to make things automagic, you could try the answer to this question. It'll work, but you'll sacrifice "F12-ability" (the ability to find and navigate through references using Visual Studio's shortcuts) and compile-time verification of your constructors and their arguments. Personally, I'd probably use the factory method.


Addendum #2

For completeness, it's also worth noting that if your service-under-test had no constructor arguments, you might also be able to use the new constraint. Based on your comments, it doesn't sound like it will work in this case, but it might be useful another time.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...