简体   繁体   English

C# - 在构造函数中键入参数 - 没有泛型

[英]C# - Type Parameters in Constructor - No Generics

I have a class that I am trying to do unit tests on. 我有一个课程,我正在尝试进行单元测试。 The class is a WCF Service Class. 该类是WCF服务类。 (Making it a generics class is not my goal.) (将它作为泛型类不是我的目标。)

I have a data access layer (DAL) type (called UserDAL) that is instantiated in many methods. 我有一个数据访问层(DAL)类型(称为UserDAL),它在许多方法中实例化。 To get these methods under test, I need to get this local variables mocked. 为了使这些方法得到测试,我需要模拟这个局部变量。 (Each instance of UserDAL has method specific value in it, so changing it a class level variable would result in messy code, so I would rather not do that.) (UserDAL的每个实例都有特定于方法的值,因此将其更改为类级变量会导致代码混乱,所以我宁愿不这样做。)

What I am thinking would be nice is to overload the constructor and pass in a type to use in the local methods. 我认为很好的是重载构造函数并传入一个类型以在本地方法中使用。 The empty param constructor would still create a normal UserDAL, but the overloaded one would have a mock type that implements IUserDAL. 空param构造函数仍然会创建一个普通的UserDAL,但是重载的那个将有一个实现IUserDAL的mock类型。

I am not sure of the syntax to say I want to pass in a type. 我不确定语法是否要传递一个类型。 Note that I am not trying to pass in a variable, but a type. 请注意,我不是要传递变量,而是传递类型。

Example: 例:

public class MyWCFClass: IMyWCFClass
{
    private TypeParam _myUserDALType;
    public MyWCFClass()
    {
        _myUserDALType = UserDAL;
    }
    public MyWCFClass(TypeParam myUserDALType)
    {
        _myUserDALType = myUserDALType;
    }

    //methods to use it
    public MyMethod()
    {
        IUserDAL userDAL = new _myUserDALType();
        //Call method in IUserDAL
        userDAL.CreateUser();
    }


    // Several similar methods that all need a different UserDAL go here
    .....
}

So, I don't know what kind of type TypeParam is (I made that up) or if this kind of think is even possible. 所以,我不知道TypeParam是什么类型的(我做了那个)或者这种想法是否可能。

If you have a non generics solution that would be great. 如果你有一个非常规的非泛型解决方案。

What you are really looking for is Dependency Injection, but you can do this by passing in a Type argument and then using Activator.CreateInstance(Type) to create the object when you need it. 您真正需要的是依赖注入,但您可以通过传入Type参数然后使用Activator.CreateInstance(Type)在需要时创建对象来完成此操作。

As far as doing real DI (which will make doing this testing a lot easier), I know that Spring.Net works reasonable well. 至于做真正的DI(这将使这个测试更容易),我知道Spring.Net工作得很好。

Use a Type, and use Activator.CreateInstance to instantiate it: 使用Type,并使用Activator.CreateInstance实例化它:

private Type _myUserDALType;

IUserDAL userDAL = Activator.CreateInstance(_myUserDALType) as IUserDAL;

You mean Type , using Activator.CreateInstance to create instances: 您的意思是Type ,使用Activator.CreateInstance创建实例:

public class MyWCFClass: IMyWCFClass
{
    private Type _myUserDALType;
    public MyWCFClass()
    {
        _myUserDALType = typeof(UserDAL);
    }
    public MyWCFClass(Type myUserDALType)
    {
        _myUserDALType = myUserDALType;
    }

    //methods to use it
    public void MyMethod()
    {
        IUserDAL userDAL = (IUserDAL) Activator.CreateInstance(_myUserDALType );
        //Call method in IUserDAL
        userDAL.CreateUser();
    }
}

Your real problem is not in the generics or lack thereof. 你真正的问题不在于泛型或缺乏。 Your real problem is that MyWFCClass is calling both new and the method. 你真正的问题是MyWFCClass正在调用new和方法。 As per Misko Hevery , you get the best testability by separating classes that call new from classes that implement logic. 根据Misko Hevery ,通过将调用new的类与实现逻辑的类分开,可以获得最佳的可测试性。 Instead of having MyWFCClass somehow know the type that you want to implement and using reflection, just pass the IUserDal object to the constructor, allowing the test harness to pass in a mock object when needed. 不要让MyWFCClass知道你想要实现的类型和使用反射,只需将IUserDal对象传递给构造函数,允许测试工具在需要时传入模拟对象。

If, for some reason, you can't do this and you can't use generics, then you have to do it yourself. 如果由于某种原因,你不能这样做,你不能使用泛型,那么你必须自己做。 Pass a Type object to the MyWFCClass constructor, then use reflection to find and invoke the constructor you want. Type对象传递给MyWFCClass构造函数,然后使用反射来查找和调用所需的构造函数。

If you want to pass in a type, you can use the Type object: 如果要传入类型,可以使用Type对象:

public class A
{
  public A(Type classType)
  {
    object myObject = Activator.CreateInstance(...classType...);
  }
}

public class B
{
...
}

public class C
{
  public static void main(string[] args)
  {
    A a = new A(typeof(B));
  }

} }

If IUserDAL defines the interface that your WCF service needs to get its job done, why not just take an instance of it as a constructor parameter? 如果IUserDAL定义了WCF服务完成其工作所需的接口,为什么不将它的实例作为构造函数参数? And since WCF requires a default constructor, why not have that default constructor call your parameterized constructor with a default implementation? 既然WCF需要默认构造函数,为什么不让默认构造函数使用默认实现调用参数化构造函数?

public class MyWCFClass : IMyWCFClass
{
    private readonly IUserDAL _userDAL;

    public MyWCFClass()
        : this(new DefaultUserDAL())
    {
    }

    public MyWCFClass(IUserDAL userDAL)
    {
        _userDAL = userDAL;
    }
}

If you're using a dependency injection container, you could expose it as a singleton and satisfy the parameterized constructor by using that singleton: 如果您正在使用依赖项注入容器,则可以将其公开为单例并使用该单例来满足参数化构造函数:

public MyWCFClass()
    this(Container.Instance.Resolve<IUserDAL>())
{
}

With this approach, your WCF class has everything it needs to get its job done, but it is still unit-testable. 使用这种方法,您的WCF类具有完成其工作所需的一切,但它仍然是可单元测试的。 Moreover, it is not responsible for creating its dependencies, which is a good thing. 而且,它不负责创建依赖项,这是一件好事。

Far simpler, and more consistent with other applications that have this problem, would be to extract an interface on UserDal, then you would have something more like: 更简单,并且与其他具有此问题的应用程序更加一致,就是在UserDal上提取接口,然后您会有更多类似的东西:

public MyWCFClass() : this(new UserDAL())
{
}
public MyWCFClass(IUserDal userDAL)
{
    _myUserDAL = myUserDAL;
}

This is also easier to use with dependency-injection frameworks than your proposed method, though that's certainly a secondary concern 这种依赖注入框架比你提出的方法更容易使用,尽管这肯定是次要问题

(Edited to clarify an alternative solution based on other comments) (编辑以澄清基于其他评论的替代解决方案)

If your DAL is essentially worthless after use because it is mutated, take a constructor with IUserDalFactory instead, with one method Create() . 如果您的DAL在使用后基本上没有价值,因为它是变异的,请使用IUserDalFactory构造函数,使用一个方法Create()

In C# there is a type called "Type". 在C#中有一种名为“Type”的类型。 With it you can create a parameter and pass in any valid type. 有了它,您可以创建一个参数并传入任何有效的类型。

private void MyMethod(Type myType)
{
  //Do something
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM