提问人:John Saunders 提问时间:1/29/2010 最后编辑:John Saunders 更新时间:4/29/2012 访问量:1803
通用约束与继承
Generic Constraints vs. Inheritance
问:
我正在尝试编写一些代码来帮助对 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
我最终决定为真实和虚假服务运行完全相同的代码。我现在正在注入一个 ,并调用或取决于我注入的属性是否为 null。IMyService
IMyService.Method
client.Method
答:
您限制了调用部分的 TClient,而不是返回类型。MakeClient<TClient>()
返回类型必须与泛型参数的类型匹配,但请想象一下:
public class MyOtherClient : ClientBase<IMyContract>, IMyContract
{
public void Method()
{
Channel.Method();
}
}
这也是通过调用 的有效返回,它不可转换为,因为它应该返回 .MakeClient<MyOtherClient>
MyClient
MyOtherClient
请注意,将返回值更改为:
return new MyClient() as TClient;
可能会让它通过编译器,但在我上面的场景中在运行时为 null。
这应该可以解决您的问题。
static T Make<T>() where T : IConvertible
{
var s = "";
return (T)(Object)s;
}
评论
基本上,您的代码可以归结为:
public static T MakeFruit<T>() where T : Fruit
{
return new Apple();
}
这始终返回一个苹果,即使您调用 .但需要归还香蕉,而不是苹果。MakeFruit<Banana>()
MakeFruit<Banana>()
泛型类型约束的含义是调用方提供的类型参数必须与约束匹配。因此,在我的示例中,您可以说但不是因为 Tiger 不符合 T 必须可转换为 Fruit 的约束。我想你认为约束意味着别的东西;我不确定那是什么。MakeFruit<Banana>()
MakeFruit<Tiger>()
可以这样想。形式参数具有形式参数类型。形式参数类型限制用作参数的表达式的类型。所以当你说:
void M(Fruit x)
你是说“在 M 中为形式参数 x 传递的参数必须可转换为 Fruit”。
泛型类型参数约束完全相同;它们限制了可以为泛型类型参数传递哪些类型参数。当你说“where T : Fruit”时,这与在形式参数列表中说(Fruit x)是一样的。T 必须是指向 Fruit 的类型,就像 x 的参数必须是指向 Fruit 的参数一样。
为什么你一开始就想有一个通用方法?我不明白你到底想用这种方法建模什么,或者你为什么希望它是通用的。
评论