【问题标题】:why would you use Func<string> instead of just string?为什么要使用 Func<string> 而不仅仅是字符串?
【发布时间】:2019-12-11 15:30:40
【问题描述】:

你为什么要使用Func&lt;string&gt; 而不仅仅是 string

我的问题是关于this repo。

有问题的行是 22:

    private static Func<string> getToken = () => Environment.GetEnvironmentVariable("GitHubToken", EnvironmentVariableTarget.Process);

getToken封装了一个没有参数的方法,返回一个string不简单地将这个变量输入为字符串的原因是什么?

你为什么要使用Func&lt;string&gt; 而不仅仅是 string

【问题讨论】:

  • 你为什么要使用Func 而不仅仅是字符串? 基于意见...去问问作者他为什么使用
  • 完全不基于意见 - 在容器环境中,所有无服务器应用程序都是,传播配置的最简单方法是通过环境变量。这显然可以改变。
  • @Selvin 不基于意见 - 在容器环境中,环境变量是首选的配置方法
  • 下来,伙计们。我们也许可以在上面代码的特定范围内回答这个问题,这似乎是一些容器的东西。然而,这个问题的发布方式让我认为它的范围比上面提到的要广泛得多。在这种情况下,不可能给出一个涵盖所有问题的体面答案。
  • 委托在下一个声明中被立即调用。所以不,避免字符串是没有意义的。这不是一个好习惯,尤其是对于配置代码,这个程序员不习惯调试他的代码。

标签: c# .net azure-functions


【解决方案1】:

我可以看到人们可能会这样做的五个原因:

  1. 当您希望允许的值不仅会随时间变化(您仍然可以通过属性来实现这一点!),而且您需要检查以找到它的位置可能会发生变化。例如,默认情况下您检查一个环境变量,但在某处有一个用户选项将改为查看配置文件、Web 服务或数据库表等。现在您可以换出函数调用,而不是检查每次在必须知道如何执行所有这些操作的函数中配置选项。
  2. 它使在async 上下文中使用它变得非常容易,您可以在其中等待结果。您可以直接在任务中等待函数,而不必将属性也包装为可等待方法。
  3. 他们想要的是函数语义而不是属性语义。这是一个选择,他们希望将函数调用呈现为类型而不是属性的公共 API。那么,lambda(实际上是:expression bodied member)只是编写该方法的一种更短的方式。
  4. 这是在新的default interface implementations in C# 8 之前编写的,其中代码提供了默认实现,但确实希望您“覆盖”它并用您自己的方法替换该方法,以便查看您需要的任何值。或者,即使他们使用的是 C# 8,额外的接口也被认为不值得。
  5. 在依赖注入场景中更改实现更容易,尽管希望这样做的人改用接口。通常注入整个类型,而不是单个方法。

【讨论】:

  • 这个...hmmmm 的 DIM。这是一个 nice 的特点。它可能应该作为示例添加到 DIM 标记
【解决方案2】:

通过将其存储为Func&lt;string&gt;,每次访问变量时都会调用Environment.GetEnvironmentVariable。这意味着如果 Environment.GetEnvironmentVariable 在后续调用中返回不同的值,您将获得新值。

据我所知,Environment.GetEnvironmentVariable 为同一输入返回不同值的唯一方法是调用 Environment.SetEnvironmentVariable

【讨论】:

  • 问题是关于 Azure Functions,一个基于容器的环境,在多个主机上运行多个实例。使用环境变量可能是最常见的分发配置的方式
  • 实际上这是唯一可以说明为什么它是Func&lt;string&gt; ... azure 函数的主机使用 1) 相同进程和 2) Environment.SetEnvironmentVariable 在其他情况下简单的static string envvar = Environment.GetEnvironmentVariable("GitHubToken"); whould够了
  • 你也可以把检查环境变量的代码放在属性getter中,不需要Func&lt;string&gt;
  • @JoelCoehoorn 是的,你可以。但是,我倾向于不将可能引发异常的东西放入属性中。 GetEnvironmentVariable 可以根据文档抛出 SecurityException。可能不太可能,所以在这种情况下差异很小。
  • 你是对的。我将存储库限制为一个环境变量,以限制我使用的 Azure 服务的数量。将其替换为 Azure KeyVault,情况可能会有所不同。想想 API 密钥发生了变化,而有人刚刚更新了 keyvault。
【解决方案3】:

这里是仓库的作者。

使用它的主要原因是类是静态的,而属性是静态的。虽然环境变量可能会发生变化,但在实际应用程序重新启动之前它不会得到反映。

我这样做的主要原因是它永远不应该在环境变量中。它应该保存在像 Azure KeyVault (AWS HSM?) 这样的秘密服务存储中。该值可能会更改,恕不另行通知,无需重新启动应用程序,并且会继续使用旧令牌,直到发生错误。

在这种情况下需要做的只是将实现功能替换为能够正确保护您的数据的功能。

【讨论】:

  • 您说“环境变量可能会改变,但在重新启动应用程序之前不会反映出来” - 您确定吗?如果有人调用 Environment.SetEnvironmentVariable,那么即使进程没有重新启动,值也会改变(如应用程序所见)according to the documentation
  • @maxime,你是怎么找到这篇文章的?
  • @mason 问题是环境变量可能由于外部原因而发生变化,不包括 .NET 运行时。通过门户等设置变量 99% 的时间,静态变量都可以。如果你想涵盖密钥过期和替换,你需要一些更动态的东西。
  • 所以我的回答中的#4,然后;)
【解决方案4】:

通常,如果您的函数产生的值可能在程序执行期间发生变化,您将使用Func&lt;T&gt; 而不是T

它对于 DI 场景也很有用,在这种场景中,函数的使用者不关心(或不应该知道,...)值的来源。它只需要能够生成当前正确的那个。

不过,在您的示例中似乎并非如此。在那里,您可能只需要一个 private static string token = ... 就可以得到相同的结果。

【讨论】:

  • 这忽略了来自 Environment.GetEnvironmentVariable 的值发生变化的情况,这可能是它没有被硬编码的原因。
【解决方案5】:

使用环境变量进行配置是容器环境中的常见模式。这些变量预计会发生变化,因此使用Func&lt;string&gt; 提取最新变量是有意义的。容器本身不需要知道关于配置存储的任何信息,或者更改是如何进行或传播的。

这些更改由编排器或主机传播到需要该特定设置的所有容器。这将允许不同的容器组使用不同的设置工作,为不同的租户提供服务,或者......也许可以使用不同的 Github 帐户?

容器应用程序仍然没有注意到这一切。

AWS Lambda 和 Azure Functions 都是基于容器的,所以这种模式很有意义。

为什么选择 Func 而不是字符串属性?

这有点基于意见。如果一个人更喜欢函数式编程,那么传递和注入单一用途的函数比传递具有单个属性或更糟糕的多个可能不相关的属性的对象要好。

【讨论】:

  • 嗯。您仍然可以使用属性来执行此操作。
  • @JoelCoehoorn 问题是为什么使用Func&lt;string&gt; 而不是静态的string。不是如何访问 env 变量。我猜作者更喜欢函数式编程。单个函数也比对象+属性更容易注入
【解决方案6】:

我的猜测是它试图传达意图。

在执行一个方法时,您会更加意识到可能会引发错误,或者可以做一些事情来更改值,在这种情况下是环境配置。

【讨论】:

    猜你喜欢
    • 2015-12-24
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 1970-01-01
    • 2012-08-26
    相关资源
    最近更新 更多