在 C# 中 if (condition) then apply function that return same type 的功能等价物是啥?
Posted
技术标签:
【中文标题】在 C# 中 if (condition) then apply function that return same type 的功能等价物是啥?【英文标题】:What is the functional equivalent of if (condition) then apply function that returns same type in C#?在 C# 中 if (condition) then apply function that return same type 的功能等价物是什么? 【发布时间】:2021-11-01 16:03:31 【问题描述】:我最近在我的一些 C# 项目中尝试了适度的函数式方法,并且在简化和精简大部分代码方面取得了一些成功。
我的问题是:
在 C# 中,是否有一种既定的“函数式”方式将 条件 转换(Func
或者:
有充分的理由不这样做吗?
例如,假设我在某处有这块砖,我希望它更“实用”:
public static string DoSomethingWith(string str)
if (str.StartsWith("!"))
str = str.ToUpper();
if (str.EndsWith("#"))
str = str + str;
return str;
我可以像这样介绍非常具体、不可重复使用的中间步骤:
public static string DoSomethingWith(string str)
=> str
.GetAllCapsIfStartsWithExclamationMark()
.GetRepeatedIfEndsWithHash();
public static string GetAllCapsIfStartsWithExclamationMark(this string str)
=> str.StartsWith("!") ? str.ToUpper() : str;
public static string GetRepeatedIfEndsWithHash(this string str)
=> str.EndsWith("#") ? str + str : str;
但这并没有真正以任何有意义的方式改善任何事情,不是吗?
但是,如果我引入一个通用扩展,例如:
public static T IfApply<T>(this T obj, Func<T, bool> predicate, Func<T, T> func)
=> predicate(obj) ? func(obj) : obj;
我可以走了:
public static string DoSomethingWith(string str)
=> str
.IfApply(x => x.StartsWith("!"), x => x.ToUpper())
.IfApply(x => x.EndsWith("#"), x => x + x);
...此时我会质疑DoSomethingWith
是否存在的必要性,除非它被重用,从而进一步减少代码中的混乱。很清楚,很简洁,很容易理解,而且有很多文档本身(首先要了解推动转型的业务需求)。显然代码(及其背后的逻辑)并不总是这么简单,但在许多情况下它看起来仍然非常有用/方便。
我一直在寻找类似的东西,但我的搜索结果一无所获。这是为什么?这只是做事的坏方法吗?它是非 Csharpish 吗?异端?
感谢您的任何见解:)
【问题讨论】:
我不明白你的最后一个问题。最后一段代码有什么问题? 罗伯特·马丁在他的《清洁代码》一书中指出“名称很重要”。如果方法 DoSomethingWith 的名称可以很容易地清楚它在做什么,那么无论您在哪里使用该方法,代码都会更容易理解。在低级别上,是的,您正在将字符串更改为大写字母,.IfApply(x => x.StartsWith("!"), x => x.ToUpper())
就足够了,但在更高级别上,方法可能是“转换为规范化形式”或“准备远程服务器请求”和表明这会有所帮助的名称。
我同意,名字很重要。我努力使用有意义的名称和所有其他 OOP 原则使我的代码自我记录。但是,正如您所指出的,如果方法中的代码足够简单(并且没有被重用),那么少一个方法有时比一个名称稍微有意义的超简单单行方法更有意义。例如,我认为使用 str.GetRepeatedIfEndsWithHash()
比使用 str.IfApply(x => x.EndsWith("#"), x => x + x)
没有任何好处,即使我不必浪费空间来定义前者。
【参考方案1】:
我喜欢你的IfApply
方法。
至于为什么在C#中不经常使用这个,让我们直接比较你的第一个和最后一个例子:
// first example (slightly condensed, but still valid C#)
public static string DoSomethingWith(string str)
if (str.StartsWith("!")) str = str.ToUpper();
if (str.EndsWith("#")) str = str + str;
return str;
// final example
public static string DoSomethingWith(string str)
=> str
.IfApply(x => x.StartsWith("!"), x => x.ToUpper())
.IfApply(x => x.EndsWith("#"), x => x + x);
如您所见,您可以以与最终示例类似的简洁方式编写您的第一个示例。因此,IfApply
方法通常不需要。是的,编写单行 if
语句可能会违反您当前的样式指南,但样式指南是创建更具可读性代码的工具,而不是目的本身。
【讨论】:
有趣。我认为您的答案可能会被我的示例的简单性所误导,特别是因为应用某事物的决定是基于参数本身。在一个更现实的场景中,我发现自己创建了具有范围内其他变量的长 LINQ 查询组合,在其中的某个地方我必须有条件地应用集合的一些转换。所以我最终定义了一个像.ConditionallyDoXYZ(condition)
这样的函数,它本质上归结为.IfApply(condition, DoXYZ)
的一个非泛型(= 不可重用= 混乱)实例。
想象一下if (col.Transform1().Transform2().Transform3().Count(Condition) > 3)
。现在想象一下,我需要使.Transform2()
的应用程序依赖于someBool
。使用稍微简化的IfApply
(使用bool
参数而不是Func<T, bool>
),这可能变成if (col.Transform1().IfApply(someBool, x => x.Transform2()).Transform3().Count(Condition) > 3)
。依然简洁明了。但是,尝试使用标准程序 if
来解决这种情况会迫使您引入几行额外的代码,包括。新的一次性变量,对吗?丑陋的恕我直言。
@JacekKołodziejek:确实如此。它是否丑陋可能是一个品味问题 - 正如克里斯托弗在问题 cmets 中提到的那样,命名良好的中间一次性变量或方法实际上可能提高可读性,例如if (Frobnicate(col).Count(Condition) > 3)
清楚地表明,将这些转换应用于 col 会使其失效。正如我所说,我确实喜欢您的 IfApply
方法,我认为您在其他任何地方都没有找到它的原因是因为 C# 具有命令式历史,而这种纯粹的函数式风格(还)不是惯用的。
同意你在这里所说的一切:)。但是,我确实想知道为什么我真的在任何地方都找不到它。我的意思是,关于用一个 LINQ 查询拯救公主的模因至少有 5 年的历史,我认为在网上进行一些搜索会给我带来任何结果,即使是一个 SO 问题的形式,其答案指向包含一些这样的 NuGet 包方法,几年前创建并下载了几百次......让我觉得我真的用这种方法做了一些非常错误的事情。
@JacekKołodziejek:我不认为你做错了什么,只是 C# 的一些不典型的东西。例如,IfApply
是一个带有不受约束的“this parameter”的扩展方法,即它将出现在所有对象上。当然,它有效,但我倾向于在 C# 中避免这种情况,尽管我无法真正解释原因(可能是因为我不愿意“污染”“全局命名空间”)。以上是关于在 C# 中 if (condition) then apply function that return same type 的功能等价物是啥?的主要内容,如果未能解决你的问题,请参考以下文章
在 thinkphp中的<if>condition条件中可以用IN 么?
C语言常见问题:Remove this conditional structure or edit its code blocks so that they‘re not all the sam