【问题标题】:In C#, Is it possible to refactor my Post and Put methods to eliminated code duplication?在 C# 中,是否可以重构我的 Post 和 Put 方法以消除代码重复?
【发布时间】:2020-10-30 02:34:03
【问题描述】:

我有以下两种方法:

        private string Post(string url, ByteArrayContent content, AuthenticationToken token = null) {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            using HttpClient client = new HttpClient();
            if (token != null) {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Access_token);
            }

            return client.PostAsync(url, content)
                .Result.Content.ReadAsStringAsync()
                .Result;
        }

        private string Put(string url,  ByteArrayContent content,  AuthenticationToken token) {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            using HttpClient client = new HttpClient();
            if (token != null) {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Access_token);
            }

            return client.PutAsync(url, content)
                .Result.Content.ReadAsStringAsync()
                .Result;
        }

如您所见,唯一的区别是一个方法调用PostAsync,而另一个方法调用PutAsync

是否可以编写一个函数,例如:

        private string Send(string url,  ByteArrayContent content,  AuthenticationToken token, String functionName) {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            using HttpClient client = new HttpClient();
            if (token != null) {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Access_token);
            }

            return client[sendFunction](url, content)
                .Result.Content.ReadAsStringAsync()
                .Result;
        }

然后我就可以将其他每个功能变成一个衬里,例如:

private string Post(string url, ByteArrayContent content, AuthenticationToken token = null) {
    this.Send(url, content, token, "PostAsync");
}

...如果我能以一种类型安全的方式传递函数或函数名,那就更好了。

【问题讨论】:

  • 类似this?
  • 类似的东西。如果我可以简化消费者方面的参数,这样就更好了,这样它就可以表达成Common(url, content, token, HttpClient.PutAsync) 之类的东西,或者消除每次迭代 url 和内容 3 次的需要。
  • AFAIK 在 C# 中没有“thiscall delegate”的概念。我认为this 是最大值
  • @Selvin,为您的帮助喝彩。我想我更喜欢我做得更好一点,但我仍然想看看是否有所改进......我会等一下,看看是否有人比我们中的任何一个都更了解。 ;-)

标签: c# rest functional-programming refactoring code-duplication


【解决方案1】:

您可以使用switch 表达式。它在 C#8 中变得出奇地实用。您可以在此处阅读更多内容并找到示例:

https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#switch-expressions

更新:考虑使用枚举而不是函数名来使您的代码类型安全。

【讨论】:

  • 为响应干杯,但 Robert Cecil Martin 将 switch 描述为令人憎恶的东西,如果必须使用它,应该深深地埋在代码中。 :-) 我正在寻找一种多态的解决方案,或者至少可以在没有分支逻辑的情况下完成。
  • 但是 Robert Cecil Martin 将 switch 描述为一种可憎的东西,如果必须使用它,应该深深地埋在代码中 ...当你有 @987654323 时,这太有趣了@ 这是最糟糕的,远远的,最糟糕的
  • 我承认这不会带来愉快的阅读,但这是我必须使用的 DSL。我可以提取一些变量,但我想不出任何可以在此上下文中添加有用信息的名称。如果您有更好的方法,请告诉我。\
【解决方案2】:

进一步研究 Selvin 的回应,我想出了:

        private string Post(string url, ByteArrayContent content, AuthenticationToken token = null) {
            return this.Send(token, (client) => client.PostAsync(url, content));
        }

        private string Put(string url,  StringContent content,  AuthenticationToken token) {
            return this.Send(token, (client) => client.PutAsync(url, content));
        }

        private string Send(AuthenticationToken token,  Func<HttpClient, Task<HttpResponseMessage>> sendFunction) {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            using HttpClient client = new HttpClient();
            if (token != null) {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Access_token);
            }

            return sendFunction(client)
                .Result.Content.ReadAsStringAsync()
                .Result;
        }

...这几乎是我想要的,但我仍然需要表达两次“客户”。

C# 是否有任何符号可以让我删除这种冗余?

或者我应该接受它“尽可能好”?

【讨论】:

  • 可以通过使用HttpClient.SendAsync 而不是 PostAsync 和 PutAsync 并仅提供 HttpMethod.Post 或 HttpMethod.Put 作为参数来删除冗余。
  • @ckuri,这确实比删除冗余更具交易性,因为那时我需要冗余调用添加参数的代码。而且使用 PostAsync 和 PutAsync 更有表现力。
【解决方案3】:

我假设由于某种原因您不能使用 async/await 运算符,

您可以像这样减少冗余:

    public static class Ext
    {
        public static string Finalize(this Task<HttpResponseMessage> httpResponseMessage)
        {
            return httpResponseMessage
                .Result
                .Content
                .ReadAsStringAsync()
                .Result;
        }
    }

    public class Program
    {
        private string Post(string url, ByteArrayContent content, AuthenticationToken token = null)
        {
            return this.CreateClient(token)
                .PutAsync(url, content)
                .Finalize();
        }

        private string Put(string url, StringContent content, AuthenticationToken token)
        {
            return this.CreateClient(token)
                .PostAsync(url, content)
                .Finalize();
        }

        private HttpClient CreateClient(AuthenticationToken token)
        {

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            using HttpClient client = new HttpClient();
            if (token != null)
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Value);
            }

            return client;
        }
    }

因此,将客户端创建包装在一种方法中,并将 Finalizing 你的异步内容包装到扩展方法中

【讨论】:

  • 我不喜欢静态方法,但这是一种有趣的方法。我不熟悉“this”关键字的这种用法,但我想我理解它。另一方面,我不完全清楚系统如何知道将 Ext 类应用于 .Finalize() 方法?我错过了什么吗?
  • 系统应用 Ext 类,因为它是扩展方法。你可以在这里阅读更多关于它们的信息:docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
  • 如果我理解正确,这类似于其他语言可能称为“特征”或“混合”的东西?但是 C# 是如何知道将扩展类应用到程序类的呢?我没有看到任何明确的声明。还是受 Ext 的可见性控制?
  • static .. (this.. ) 方法(扩展方法)在您使用的 napespace 中应用(当前 od 由 using 应用),就像用新方法扩展预定义类型一样
猜你喜欢
  • 2013-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-09
相关资源
最近更新 更多