2010-01-29 147 views
-1

我尝试写一些代码,以帮助单元测试WCF服务。这些服务通过创建代理实例的Facade类访问,然后调用代理方法并返回结果;为每个代理方法。我希望能够用创建真实服务或虚假服务的东西替换当前的创建代码。泛型约束与传承

我不能得到那个工作。我煮它归结为以下几点:

using System.ServiceModel; 

namespace ExpressionTrees 
{ 
    public interface IMyContract 
    { 
     void Method(); 
    } 

    public class MyClient : ClientBase<IMyContract>, IMyContract 
    { 
     public MyClient() 
     { 
     } 

     public MyClient(string endpointConfigurationName) 
      : base(endpointConfigurationName) 
     { 
     } 

     public void Method() 
     { 
      Channel.Method(); 
     } 
    } 

    public class Test 
    { 
     public TClient MakeClient<TClient>() 
      where TClient : ClientBase<IMyContract>, IMyContract, new() 
     { 
      return new MyClient("config"); 

      // Error: 
      // Cannot convert expression of type 'ExpressionTrees.ServiceClient' to return type 'TClient' 
     } 
    } 
} 

为什么说,即使MyClient类从ClientBase<IMyContract>派生并实现IMyContract,我不能想回一个TClient的方法返回一个MyClient实例? TClient指定了一个类型约束,我认为这意味着同样的事情。


我的目标是调用代码:

public void CallClient<TClient>() 
     where TClient : ClientBase<IMyContract>, IMyContract 
    { 
     TClient client = null; 
     bool success = false; 
     try 
     { 
      client = MakeClient<TClient>(); 
      client.Method(); 
      client.Close(); 
      success = true; 
     } 
     finally 
     { 
      if (!success && client != null) 
      { 
       client.Abort(); 
      } 
     } 
    } 

但不是始终在呼唤MakeClient<TClient>,我希望能够有一个单元测试注入模拟对象。由于上面的代码取决于ClientBase<IMyContract>,IMyContract,所以我似乎在试图“合成”一个能够满足这个约束的泛型类。

现在回想起来,这是没有意义的。作为一个例子,所述ClientBase<IMyContract>会期望在一个Channel对象将被构造,它可以然后委托Close方法这样的方式来进行实例化。

我已经结束了撑船具有完全相同的代码运行无论对于真实和虚假服务。我现在正在注入一个IMyService,并调用IMyService.Methodclient.Method,具体取决于我的注入属性是否为空。

+1

我仍然无法想象为什么这个问题吸引了downvotes。我希望其中一位下台者会告诉我,以便我可以改进这个问题。 – 2013-12-21 01:59:28

回答

8

基本上你的代码归结为:

public static T MakeFruit<T>() where T : Fruit 
    { 
     return new Apple(); 
    } 

这总是返回一个苹果,即使你打电话MakeFruit<Banana>()。但MakeFruit<Banana>()需要返回一个香蕉,而不是一个苹果。

泛型类型约束的含义是,通过调用者提供的类型参数必须与约束相匹配。因此,在我的示例中,可以说MakeFruit<Banana>(),但不是MakeFruit<Tiger>(),因为Tiger不符合T必须可转换为Fruit的约束条件。我认为你相信约束意味着别的东西;我不确定那是什么。

想想看这样的。一个形式参数有一个正式的参数类型。形式参数类型限制用作参数的表达式的类型。所以,当你说:

void M(Fruit x) 

你说“在并购正式参数x传递的参数必须转换到果子”。

通用类型参数约束是完全相同的;它们是对通用类型参数可能传递什么类型参数的限制。当你说“Where T:Fruit”时,这与正式参数列表中的(Fruit x)相同。 T必须成为Fruit的一种类型,就像x的论点必须成为Fruit的论点一​​样。

为什么你连想都摆在首位的通用方法是什么?我不明白你试图用这种方法建模的原因,或者你为什么希望它是通用的。

+0

埃里克,我会更深入一步,因此我很清楚自己想要做什么。 – 2010-01-29 03:08:10

3

您被限制TClient呼叫,而不是返回类型的MakeClient<TClient>()部分。

返回类型必须匹配泛型参数的类型,但拍下这一刻:

public class MyOtherClient : ClientBase<IMyContract>, IMyContract 
{ 
    public void Method() 
    { 
     Channel.Method(); 
    } 
} 

这也是通过调用MakeClient<MyOtherClient>有效的回报,这MyClient没有可转化到,因为它应该返回一个类型的MyOtherClient

注意,更改回:

return new MyClient() as TClient; 

可能会得到它过去的编译器,但在运行时在我的情况下空之上。

0

这应该可以解决您的问题。

static T Make<T>() where T : IConvertible 
{ 
    var s = ""; 
    return (T)(Object)s;   
} 
+0

不幸的不是。我真的需要一个参数化的构造函数。我更新了我的问题以表明这一点。 – 2010-01-29 01:39:28