通用约束与继承

时间:2010-01-29 01:07:44

标签: c# wcf generics mocking

我正在尝试编写一些代码来帮助单元测试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实例返回TClientTClient指定了我认为意味着相同的类型约束。


我的目标是调用这样的代码:

    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

3 个答案:

答案 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;        
}