为什么API会返回’void’?

在编写API或可重用对象时,是否存在任何技术原因导致返回’void’的所有方法调用不应仅返回’this’(*在C ++中为此)?

例如,使用字符串类,我们可以做这样的事情:

string input= ...; string.Join(input.TrimStart().TrimEnd().Split("|"), "-"); 

但我们不能这样做:

 string.Join(input.TrimStart().TrimEnd().Split("|").Reverse(), "-"); 

..beause Array.Reverse()返回void。

还有许多其他示例,其中API具有许多返回空白的操作,因此代码最终看起来像:

 api.Method1(); api.Method2(); api.Method3(); 

..但完全有可能写:

 api.Method1().Method2().Method3() 

..如果API设计师允许这样做。

遵循这条路线有技术原因吗? 或者它只是一种风格的东西,表示可变性/新对象?

(x-ref 关于返回无效的文体问题 )


结语

我接受了Luvieere的答案,因为我认为这最能代表意图/设计,但似乎有一些流行的API示例与此不同:

在C ++中cout << setprecision(..) << number << setwidth(..) << othernumber; 似乎改变了cout对象,以便修改插入的下一个数据。

在.NET中, Stack.Pop()Queue.Dequeue()都返回一个项目,但也改变了集合。

ChrisW和其他人的道具,详细了解实际的性能成本。

返回void状态的方法更明确,它们有副作用。 返回修改结果的那些应该没有副作用,包括修改原始输入。 使方法返回void意味着它更改其输入或API的其他内部状态。

如果你有Reverse()返回一个string ,那么对于API的用户来说,它是否返回一个新的字符串或同一个string就不会显而易见。

 string my_string = "hello"; string your_string = my_string.reverse(); // is my_string reversed or not? 

这就是为什么,例如,在Python中, list.sort()返回None ; 它将就地排序与sorted(my_list)区分开来。

遵循这条路线有技术原因吗?

C ++设计指南之一是“不支付你不使用的function”; 返回this将会有一些(轻微的)性能损失,这是一个很多人(我,一个人)不愿意使用的function。

许多其他人提到的技术原理( void强调函数具有副作用的事实)被称为命令查询分离 。

虽然这个原则有利有弊,例如(主观地)更清晰的意图与更简洁的API,但最重要的部分是保持一致

我想象一个原因可能是简单。 很简单,API通常应该尽可能小。 应该清楚它的每个方面,它是什么。

如果我看到一个返回void的函数,我知道返回类型并不重要。 无论函数做什么,它都不会返回任何东西供我使用。

如果一个函数返回一些非void东西,我必须停下来想知道为什么 。 这个对象可能会返回什么? 为什么要归还? 我可以假设this 总是返回,或者它有时会为空吗? 还是一个完全不同的对象? 等等。

在第三方API中,如果从未出现过这类问题,我更愿意。

如果函数不需要返回任何东西,它不应该返回任何东西。

如果您打算从F#调用您的API,请返回void除非您确信这个特定的方法调用几乎每次都被链接到另一个。

如果您不关心使用F#轻松使用API​​,可以在这里停止阅读。

在某些方面,F#比C#更严格 – 它希望你明确是否要调用方法来获取值,或者纯粹是因为它的副作用。 因此,当该方法也返回值时调用其副作用的方法变得很笨拙,因为必须显式忽略返回的值以避免编译器错误。 这使得“流畅的界面”在F#中使用起来有些尴尬,F#拥有自己的优良语法,可以将一系列调用链接在一起。

例如,假设我们有一个带有Log方法的日志系统,该方法返回this方法以允许某种方法链接:

 let add xy = Logger.Log(String.Format("Adding {0} and {1}", x, y)) // #1 x + y // #2 

在F#中,因为第1行是一个返回值的方法调用,并且我们没有对该值执行任何操作,所以add函数被认为采用两个值并返回该Logger实例。 但是,第2行不仅返回一个值,它出现在F#认为是函数的“return”语句之后,实际上有两个“return”语句。 这将导致编译器错误,我们需要显式忽略Log方法的返回值以避免此错误,以便我们的add方法只有一个return语句。

 let add xy = Logger.Log(String.Format("Adding {0} and {1}", x, y)) |> ignore x + y 

正如你可能猜到的那样,制作大量关于副作用的“Fluent API”调用变得有点令人沮丧,因为在整个地方散布了大量的ignore语句。

当然,您可以充分利用这两个方面,并为C#和F#开发人员提供流畅的API和F#模块,以便使用您的代码。 但是如果您不打算这样做,并且您打算将API用于公共消费, 在从每种方法返回之前再三考虑。

除了设计原因外,还有一点性能成本(速度和空间)。