我正在尝试编写一些代码来帮助单元测试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
,我也无法从方法返回MyClient
实例返回TClient
? 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.Method
或client.Method
。
答案 0 :(得分: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)
你说的是“M中形式参数x传递的参数必须可以转换为Fruit”。
通用类型参数约束完全相同;它们限制了可以为泛型类型参数传递的类型参数。当你说“where T:Fruit”时,这与形式参数列表中的说法(Fruit x)相同。 T必须是一个属于Fruit的类型,正如x的参数必须是一个属于Fruit的论据。
为什么你甚至想要首先使用通用方法?我不明白你究竟想用这种方法建模什么,或者为什么你希望它是通用的。
答案 1 :(得分:3)
您正在限制呼叫的MakeClient<TClient>()
部分中的TClient,而不是返回类型。
返回类型必须与泛型参数的类型匹配,但请注意:
public class MyOtherClient : ClientBase<IMyContract>, IMyContract
{
public void Method()
{
Channel.Method();
}
}
通过调用MakeClient<MyOtherClient>
,MyClient
无法转换为MyOtherClient
,这也是一个有效的回报,因为它应该返回return new MyClient() as TClient;
类型。
请注意,将返回更改为:
{{1}}
可能会通过编译器,但在我上面的场景中在运行时为空。
答案 2 :(得分:0)
这可以解决您的问题。
static T Make<T>() where T : IConvertible
{
var s = "";
return (T)(Object)s;
}