我从不喜欢out
和ref
参数。当我看到它们在行动时,它们让我觉得设计上有些混乱。
我认为唯一的例外是所谓的TryXXX模式,它返回一个布尔值作为函数结果(无论一切都很好或出了什么问题)和一个out参数用于实际结果,直到我读到this article今天,它让我想到是否有更好的模式来实现这种方法。
我认为我们可以有一个返回多个结果的函数(或者文章中说的是一个元组)
Tuple<Exception,T> TryParseT(object obj)
或接受成功回调函数的函数:
void TryParseT(object obj,Action<T> success)
问题是,从功能设计的角度来看哪一个更好?
更新: 为了重新解释我的问题,我想知道这两个函数中的哪一个更符合函数式编程原理,为什么?
答案 0 :(得分:1)
基本上问题是,遵循函数式编程方法,您应该始终为输入值提供返回值。因此返回的void
路线不是最佳选择。您需要返回一个可以表示成功(并保持成功结果)和失败(并且不保留结果)的值。
最接近的是您返回Tuple
的地方,其中包含例外情况。然而,你没有基础设施&#39;一旦你得到它,就可以可靠地处理Tuple
。所以围绕它的代码脚手架将重复。
查看this library language-ext
。它涉及使用out
的实现来改善TryParse
的{{1}}问题。
Option<T>
该库还允许您检查某些内容是否有效:
string inp = "123";
// Attempts to parse the value, uses 0 if it can't
int value1 = parseInt(inp).IfNone(0);
// Functional alternative to above
// Attempts to parse the value, uses 0 if it can't
int value2 = ifNone(parseInt(inp), 0);
// Attempts to parse the value and then pattern matches the result
int value3 = parseInt(inp).Match(
Some: x => x * 2,
None: () => 0
);
// Functional alternative to above
// Attempts to parse the value and then pattern matches the result
int value4 = match( parseInt(inp),
Some: x => x * 2,
None: () => 0
);
允许比较而不实际提取值:
if( parseInt(inp) )
return 1;
else
return 0;
以及逻辑操作:
if( parseInt(inp) == 123 )
return 123;
else
return 0;
最后LINQ表达式通常可以删除if-then-else或匹配的需要:
var allValid = parseInt(x) && parseInt(y) && parseInt(z);
var someValid = parseInt(x) || parseInt(y) || parseInt(z);
它还有var res = from x in parseInt(inp1)
from y in parseInt(inp2)
from z in parseInt(inp3)
select x + y + z;
,TryGetValue
,IDictionary
和IReadOnlyDictionary
的{{1}}个附加信息,而是返回IImmutableDictionary
,可以按上述方式使用
答案 1 :(得分:0)
最优雅的方法是
int Parse(string value)
Tryxxxx方法仅适用于名为performance的实现细节。如果您正在寻求优雅,您可以使用Parse方法并通过快速失败来处理任何错误。 你可以改为返回一个元组,但由于Tuple是一个引用类型,这将在堆上花费额外的分配。
在性能方面(如果你关心),更好的解决方案是aKeyValuePair。但它隐藏了(如元组)通用数据类型背后的语义,这对于代码清晰度来说并不是最佳的。一种更好的信号失败方式,而不是通过定义元组的第一个bool包含失败状态的一些约定来定义自己的数据类型。
struct ParseResult<T>
{
public bool Success { get; private set; }
public T Value { get; private set; }
public ParseResult(T value, bool success):this()
{
Value = value;
Success = success;
}
}
class Program
{
static ParseResult<int> TryParse(string s)
{
int lret = 0;
if (int.TryParse(s, out lret))
{
return new ParseResult<int>(lret, true);
}
else
{
return new ParseResult<int>(lret, false);
}
}
static void Main(string[] args)
{
string test = "1";
var lret = TryParse(test);
if( lret.Success )
{
Console.WriteLine("{0}", lret.Value);
}
}
}
这种方法仍然非常有效,并且以分配廉价容器对象为代价省去了out参数。