【问题标题】:How to refactor "using" statement to avoid code duplication?如何重构“使用”语句以避免代码重复?
【发布时间】:2015-05-11 21:59:33
【问题描述】:

假设我有以下方法:

 public string GetSchedules(string request)
    {
        using (var soapClient = new ServiceReference1.CustomDataTimetableToolKitServicesSoapClient(EndpointConfiguratioName, Endpoint))
        {
            return soapClient.GetSchedules(AuthenticationInfo, request);
        }
    }

    public string GetCountryList(string request)
    {
        using (var soapClient = new ServiceReference1.CustomDataTimetableToolKitServicesSoapClient(EndpointConfiguratioName, Endpoint))
        {
            return soapClient.GetCountryList(AuthenticationInfo, request);
        }
    }

    public string GetCarriers(string request)
    {
        using (var soapClient = new ServiceReference1.CustomDataTimetableToolKitServicesSoapClient(EndpointConfiguratioName, Endpoint))
        {
            return soapClient.GetCarriers(AuthenticationInfo, request);
        }
    }

如您所见,唯一不同的是所调用方法的名称。我如何重构这些方法以仅应用一次“使用”语句并避免代码重复?

【问题讨论】:

  • 最佳做法是对 WCF 客户端使用 using 语句 - MSDN

标签: c# wcf refactoring using-statement


【解决方案1】:

对我来说,你所拥有的一切都很好,但如果你想排除这些因素,你可以使用 Func 和 lambdas。大致如下:

public string GetSchedules(string request)
{
    return Worker((c) => c.GetSchedules(AuthenticationInfo, request));
}

public string GetCountryList(string request)
{
    return Worker((c) => c.GetCountryList(AuthenticationInfo, request));
}

public string GetCarriers(string request)
{
    return Worker((c) => c.GetCarriers(AuthenticationInfo, request));
}

private string Worker(Func<SoapClientClassGoesHere, string> f)
{
    using (var soapClient = new ServiceReference1.CustomDataTimetableToolKitServicesSoapClient(EndpointConfiguratioName, Endpoint))
    {
        return f(soapClient);
    }
}

Func&lt;A, R&gt; 表示“一个函数,它接受 A 类型的参数并返回 R 类型的值”(对于需要 两个的函数,您可以使用 Func&lt;A, B, R&gt;论据等)。

更多关于Func&lt;&gt;this questionthis question 中的lambda(以及更多,这是一个丰富的主题)。

这是一个live example on ideone.com(一个非常愚蠢的活生生的例子,但它展示了这个概念):

using System;
using System.Collections.Generic;

class Foo {
    public string GetSchedules(string request)
    {
        return Worker((c) => c[request]);
    }

    public string GetCountryList(string request)
    {
        return Worker((c) => c[request].ToUpper());
    }

    public string GetCarriers(string request)
    {
        return Worker((c) => c[request].ToLower());
    }

    private string Worker(Func<Dictionary<string,string>, string> f)
    {
        var d = new Dictionary<string, string>();
        d.Add("1", "One");
        d.Add("2", "Two");
        d.Add("3", "Three");
        return f(d);
    }
}

public class Test
{
    public static void Main()
    {
        var f = new Foo();
        Console.WriteLine(f.GetSchedules("1"));
        Console.WriteLine(f.GetCountryList("1"));
        Console.WriteLine(f.GetCarriers("1"));
    }
}

【讨论】:

  • 您需要将request 传递给委托人,因此您需要将其传递给Worker,并且您需要更新Func 的签名。
  • @JohnSaunders:不,你没有,lambda 关闭它:ideone.com/pjxEio
  • @T.J.Crowder 非常感谢您,但是不同的响应类型呢?
  • @moathnaji - 如果你需要支持它们,你可以使 Worker generic: private T Worker&lt;T&gt;(Func&lt;Dictionary&lt;string, string&gt; T&gt; f) { /*...*/ } (我想,这不是我的想法,我还没有做过 C# 泛型几个月。)
【解决方案2】:

那里确实没有太多重复。不过,

public ServiceReference1.CustomDataTimetableToolKitServicesSoapClient NewClient()
{
    return new ServiceReference1.CustomDataTimetableToolKitServicesSoapClient(EndpointConfiguratioName, Endpoint)
}

using (var client = NewClient()) {
    return soapClient.GetCountryList(AuthenticationInfo, request);
}

此外,由于您的所有方法都采用string 参数并返回string,因此很容易编写一个方法来调用它们,将操作作为委托传递给调用。很遗憾,我现在没有时间为你写这篇文章。

【讨论】:

  • 我看不出这有什么帮助——事实上,如果它添加了一个方法?请注意,每次调用都指向不同的端点
  • @JamesSugrue:它集中了代理实例的创建。然而它需要被创建,它只会被创建在一个地方。
【解决方案3】:

你可以这样使用 lambda 函数:

public string GetCarriers(string request)
{
    return Get((authInfo, request) => soapClient.GetCarriers(authInfo, request), request);
}

...

public string Get(Func<AuthenticationInfo, string, string> action, string request) {
    using (var soapClient = new ServiceReference1.CustomDataTimetableToolKitServicesSoapClient(EndpointConfiguratioName, Endpoint))
    {
        return action(AuthenticationInfo, request)
    }
}

我不知道这是否编译,但你明白了。

编辑: 正如@Tim S. 所注意到的,这段代码可能会更短:

public string GetCarriers(string request)
{
    return Get(soapClient.GetCarriers, request);
}

...

public string Get(Func<AuthenticationInfo, string, string> action, string request) {
    using (var soapClient = new ServiceReference1.CustomDataTimetableToolKitServicesSoapClient(EndpointConfiguratioName, Endpoint))
    {
        return action(AuthenticationInfo, request)
    }
}

编辑 2:客户端超出范围。所以正确的代码是:

public string GetCarriers(string request)
{
    return Get((client, authInfo, request) => client.GetCarriers(authInfo, request));
}

...

public string Get(Func<ISoapClient, AuthenticationInfo, string, string> action, string request) {
    using (var soapClient = new ServiceReference1.CustomDataTimetableToolKitServicesSoapClient(EndpointConfiguratioName, Endpoint))
    {
        return action(soapClient, AuthenticationInfo, request)
    }
}

【讨论】:

  • 检测到维护地狱
  • @SergeyBerezovskiy:为什么会有维护问题?
  • @JohnSaunders 阅读和理解是维护的一部分
  • @SergeyBerezovskiy:但是有多少人需要阅读(并理解)该代码?此外,如果需要添加其他方法,该模式将很容易遵循。
  • (authInfo, request) =&gt; soapClient.GetCarriers(authInfo, request) 通常可以替换为soapClient.GetCarriers
【解决方案4】:

如果您的 Projekt 不是很大,您可以使用以下内容(有点乱,很容易出错):

public string getX (string request, string x)
{
    using (var soapClient = new ServiceReference1.CustomDataTimetableToolKitServicesSoapClient(EndpointConfiguratioName, Endpoint))
    {
        switch (x)
        {
            case "schedules":
                return soapClient.GetSchedules(AuthenticationInfo, request);
                break;
            case "countryList":
                return soapClient.GetCountryList(AuthenticationInfo, request);
                break;
            case "carriers":
                return soapClient.GetCarriers(AuthenticationInfo, request);
                break;
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2022-06-14
    • 1970-01-01
    • 1970-01-01
    • 2020-02-14
    • 1970-01-01
    • 2019-09-28
    • 1970-01-01
    • 1970-01-01
    • 2016-06-12
    相关资源
    最近更新 更多