2009-12-04 157 views
3

我有一个类,我试图做单元测试。该类是一个WCF服务类。 (使它成为一个泛型类不是我的目标。)C# - 构造函数中的类型参数 - 没有泛型

我有一个在许多方法中实例化的数据访问层(DAL)类型(称为UserDAL)。为了让这些方法得到测试,我需要得到这个局部变量的模拟。 (UserDAL的每个实例都有特定于方法的值,因此将其更改为类级别的变量会导致代码混乱,因此我宁愿不这样做。)

我在想的是将重载构造函数并传入一个类型用于本地方法。空的参数构造函数仍然会创建一个普通的UserDAL,但重载的会有一个实现IUserDAL的模拟类型。

我不确定说我想传入一个类型的语法。请注意,我没有试图传入一个变量,而是一个类型。

例子:

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 
    ..... 
} 

所以,我不知道是什么样的类型TypeParam的是(我编的),或者如果这种想不到的是甚至有可能。

如果您有一个非泛型解决方案,那就太棒了。

+1

我不想做泛型的原因是因为所有静态方法调用必须更新为使用通用参数(即MyWCFClass .MyStaticMethod)这需要在不应该​​了解的项目中引用我的数据访问层数据访问层。 (加上我试了一下,我得到一个对象没有设置为一个对象错误isntance。) – Vaccano 2009-12-04 21:28:06

回答

5

你的意思是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(); 
    } 
} 
+2

尽管这个解决方案正是Vaccano要求的,我鼓励解决方案克里斯和JS Bangs建议:使用InversionOfControl和一个容器(Spring.Net ,Ninject,Structuremaps,Unity,LinFu,你的名字......)以及ctor-injection。同样在上述解决方案中,如果_myUserDALType没有默认ctor,则会在运行时抛出异常。 – tobsen 2009-12-04 21:49:32

+0

这些模式的问题是他们认为变量是一个类级变量。我正在使用的是一种在本地通过多种方法实例化的类型。 (请参阅我的问题。)如果我可以将它移动到单个类级别的变量,那么所有这些想法(和其他)都将工作得很好! – Vaccano 2009-12-04 21:58:55

+0

感谢代码Marc。它效果很好! – Vaccano 2009-12-04 21:59:25

5

使用类型,并使用Activator.CreateInstance实例吧:

private Type _myUserDALType; 

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

如果你想在一个类型传递,可以使用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)); 
    } 

}

8

您真正需要的是依赖注入,但您可以通过传入Type参数,然后使用Activator.CreateInstance(Type)在需要时创建该对象。

就做真正的DI(这会使测试变得更容易)而言,我知道Spring.Net工作得很好。

0

在C#中有一个名为 “类型” 的类型。有了它,你可以创建一个参数并传入任何有效的类型。

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

您真正的问题不在于仿制药还是缺乏仿制药。你真正的问题是MyWFCClass正在调用new和该方法。根据Misko Hevery,通过将实现逻辑的类从new中分离出来,可以获得最佳的可测试性。而不是让MyWFCClass知道你想要实现并使用反射的类型,只需将IUserDal对象传递给构造函数,允许测试工具在需要时传入模拟对象。

如果出于某种原因,你不能这样做,而且你不能使用泛型,那么你必须自己做。将一个Type对象传递给构造函数MyWFCClass,然后使用反射来查找并调用所需的构造函数。

+1

+1显示“Misko Hevery的代码审查人员指南” – tobsen 2009-12-04 21:51:42

1

如果IUserDAL定义了您的WCF服务需要完成其工作的接口,为什么不把它的实例作为构造函数参数?由于WCF需要一个默认的构造函数,为什么没有这个默认的构造函数调用你的默认实现的参数化构造函数?

public class MyWCFClass : IMyWCFClass 
{ 
    private readonly IUserDAL _userDAL; 

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

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

如果您使用的是依赖注入容器,你可以公开为一个独立的,并通过使用单满足参数的构造函数:

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

通过这种方法,您的WCF类拥有一切它需要完成它的工作,但它仍然是单元测试。此外,它不负责创建它的依赖关系,这是一件好事。

+0

“此外,它不负责创建它的依赖关系,这是一件好事。”但现在它直接依赖基础设施并违反了“集装箱无知”的最佳实践,即imho。 – tobsen 2009-12-04 21:57:38

+0

如果WCF提供了一个您可以用来创建服务对象的钩子,那么您可以将其挂钩。否则,我认为这是两个邪恶中较小的一个。 – 2009-12-04 22:25:41

1

简单得多,并与有这个问题的其他应用程序更加一致,将提取的UserDal的接口,那么你将有更多的东西一样:

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

这也更容易依赖使用步喷射框架比你提出的方法,虽然这肯定是一个次要问题

(编辑澄清基于其他意见的替代解决方案)

如果你的DAL是使用后BEC基本上毫无价值ause它被改变了,取而代之的是带IUserDalFactory的构造函数,用一种方法Create()

+0

唉,这假设_myUserDAL可以是一个类级别的对象。他们的方式,我目前的结构,这是不可能的。 – Vaccano 2009-12-04 22:11:21