对不起,我的标题太含糊了——如果我能想出一个简洁的标题,我就不用问这个问题了。

假设我有一个不可变的列表类型。它有一个操作Foo(x),该操作返回一个新的不可变列表,其中指定的参数作为结尾的额外元素。因此,要建立一个包含"Hello", "immutable", "world"值的字符串列表,你可以这样写:

var empty = new ImmutableList<string>();
var list1 = empty.Foo("Hello");
var list2 = list1.Foo("immutable");
var list3 = list2.Foo("word");

(这是c#代码,我最感兴趣的是c#建议,如果你觉得语言很重要的话。这根本不是一个语言问题,但语言中的习语可能很重要。)

重要的是现有的列表不会被Foo改变,所以是空的。Count仍然返回0。

另一种(更习惯的)达到最终结果的方式是:

var list = new ImmutableList<string>().Foo("Hello")
                                      .Foo("immutable")
                                      .Foo("word");

我的问题是:Foo最好的名字是什么?

编辑3:正如我稍后所揭示的,类型的名称实际上可能不是ImmutableList<T>,这使得位置很清楚。相反,想象它是TestSuite,它是不可变的,因为它所在的整个框架是不可变的……

(编辑3结束)

到目前为止,我想到的选项是:

Add: common in .NET, but implies mutation of the original list Cons: I believe this is the normal name in functional languages, but meaningless to those without experience in such languages Plus: my favourite so far, it doesn't imply mutation to me. Apparently this is also used in Haskell but with slightly different expectations (a Haskell programmer might expect it to add two lists together rather than adding a single value to the other list). With: consistent with some other immutable conventions, but doesn't have quite the same "additionness" to it IMO. And: not very descriptive. Operator overload for + : I really don't like this much; I generally think operators should only be applied to lower level types. I'm willing to be persuaded though!

我选择的标准是:

给出方法调用结果的正确印象(即它是带有额外元素的原始列表) 尽可能清楚地表明它不会改变现有的列表 当像上面的第二个例子一样连接在一起时,听起来很合理

如果我说得不够清楚,请再问我一些细节。

编辑1:以下是我更喜欢Plus而不是Add的理由。考虑以下两行代码:

list.Add(foo);
list.Plus(foo);

在我看来(这是我个人的观点),后者显然是有问题的——它就像写“x + 5;”作为一个单独的语句。第一行看起来是可以的,直到您记得它是不可变的。事实上,加号操作符本身不改变操作数的方式是我最喜欢加号的另一个原因。没有操作符重载的麻烦,它仍然具有相同的含义,其中包括(对我来说)不改变操作数(在本例中是方法目标)。

编辑2:不喜欢添加的原因。

各种各样的回答都很有效:“使用Add。这就是DateTime所做的,而String有Replace方法等,这些方法不会使不可变性变得明显。”我同意,这里有优先权。然而,我见过很多人称之为DateTime。添加或字符串。替换并期待突变。有大量的新闻组问题(如果我仔细研究的话,可能会有这样的问题),这些问题的答案是“您忽略了String.Replace的返回值;字符串是不可变的,返回一个新的字符串"

现在,我应该揭示这个问题的一个微妙之处——类型实际上可能不是一个不可变列表,而是一个不同的不可变类型。特别是,我正在开发一个基准测试框架,在该框架中,您可以向套件中添加测试,并创建一个新的套件。这可能是显而易见的:

var list = new ImmutableList<string>();
list.Add("foo");

不会完成任何事情,但当你把它改成:

var suite = new TestSuite<string, int>();
suite.Add(x => x.Length);

看起来应该没问题。然而,对我来说,这个错误更清楚了:

var suite = new TestSuite<string, int>();
suite.Plus(x => x.Length);

这只是在乞求:

var suite = new TestSuite<string, int>().Plus(x => x.Length);

理想情况下,我希望我的用户不必被告知测试套件是不可变的。我想让他们坠入成功的深渊。这可能是不可能的,但我想试试。

很抱歉,我只讨论了不可变的列表类型,过度简化了最初的问题。并非所有的集合都像ImmutableList<T>:)那样具有自描述性。


当前回答

一些随意的想法:

ImmutableAdd () Append () ImmutableList<T>(ImmutableList<T> originalList, T newItem)构造函数

其他回答

我不认为英语语言会让你在使用与“添加”意思相同的动词时,以一种明确无误的方式暗示不变性。“加”几乎就可以了,但人们仍然会犯错误。

防止用户将对象误认为可变对象的唯一方法是通过对象本身的名称或方法的名称(如“GetCopyWith”或“CopyAndAdd”等详细选项)显式地使其显式。

所以就用你最喜欢的"加"吧

一些随意的想法:

ImmutableAdd () Append () ImmutableList<T>(ImmutableList<T> originalList, T newItem)构造函数

我把它叫做ToInclude

var empty = new ImmutableList<string>();
var list1 = empty.ToInclude("Hello");
var list2 = list1.ToInclude("immutable");
var list3 = list2.ToInclude("word");

惯用地(?)

var list = new ImmutableList<string>().ToInclude("Hello");
                                      .ToInclude("immutable");
                                      .ToInclude("word");

对你提到的案子也适用。

var list = new ImmutableList<string>();list.ToInclude("foo");

var suite = new TestSuite<string, int>();suite.ToInclude(x => x.Length);

copyWithSuffix:将是一个自定义方法名,类似于Objective-C的不可变字符串复制方法stringByAppendingString:。

var suite = new TestSuite<string>();
suite = suite.CopyWithSuffix("Str1")
             .CopyWithSuffix("Str2")
             .CopyWithSuffix("Str3");

后缀传达了它的附加性,而Copy则明确了它的不可变性。您可以自由地将Suffix替换为更符合测试套件实际类型的东西(也许您没有将这些后缀添加到一个大的内部字符串中,您可以提供我所知道的测试用例名称/参数)。

我最终选择在BclExtras中添加我所有的不可变集合。原因是,这是一个容易预测的名字。我不太担心人们会混淆Add和一个变化的Add,因为类型名的前缀是Immutable。

有一段时间,我考虑了Cons和其他函数样式名称。最后我对他们不屑一顾,因为他们没有那么出名。函数式程序员当然能理解,但他们不是大多数用户。

其他名字:你提到过:

另外:我在这件事上犹豫不决。对我来说,这并不像Add那样是一个非变异操作 使用:会导致VB出现问题(双关语) 操作符重载:可发现性将是一个问题

我考虑的选择:

Concat:字符串是不可变的,使用这个。不幸的是,它只适合加在后面 CopyAdd:复制什么?线人,名单? AddToNewList:也许是一个很好的列表。但是集合、堆栈、队列等等呢?

不幸的是,似乎真的没有一个词是这样的

绝对是一个不可变的操作 大多数用户都能理解 少于4个词即可表达

当您考虑List之外的集合时,情况就更加奇怪了。以Stack为例。即使是第一年的程序员也会告诉你堆栈有Push/Pop方法对。如果你创建了一个ImmutableStack,并给它一个完全不同的名字,让我们叫它Foo/Fop,你只是增加了更多的工作,让他们使用你的集合。

编辑:对+编辑的响应

我明白你用Plus的意思了。我认为更有说服力的情况应该是减号。如果我看到下面的内容,我肯定会想知道程序员到底在想什么

list.Minus(obj);

我对正负搭配或新搭配最大的问题是感觉太过了。集合本身已经有了一个可区分的名称,即Immutable前缀。为什么要进一步添加词汇表,其目的是添加与Immutable前缀已经做的相同的区别呢?

我可以看到call site参数。从单个表达式的角度来看,它更清楚。但在整个函数的上下文中,这似乎是不必要的。

编辑2

我同意人们肯定被String弄糊涂了。Concat和DateTime.Add。我见过几个非常聪明的程序员遇到过这个问题。

然而,我认为ImmutableList是一个不同的参数。对于程序员来说,String或DateTime没有将其建立为不可变的。你必须知道它通过其他来源是不可变的。因此,这种困惑并非出乎意料。

ImmutableList没有这个问题,因为名称定义了它的行为。你可能会说人们不知道什么是不可变的,我认为这也是有道理的。我当然是在大学二年级才知道的。但是无论您选择什么名称而不是Add,都有同样的问题。

编辑3:像TestSuite这样不可变但不包含单词的类型呢?

我认为这说明你不应该发明新的方法名。也就是说,为了便于并行操作,显然有一种使类型不可变的驱动力。如果您专注于更改集合的方法名称,那么下一步将是在您使用的每个不可变类型上更改方法名称。

我认为更有价值的做法是将类型标识为Immutable。这样,您就可以解决问题,而不必重新考虑每个突变的方法模式。

现在,您如何将TestSuite识别为不可变的呢?在今天的环境下,我认为有几种方法

前缀Immutable: ImmutableTestSuite 添加一个描述不可变级别的属性。这当然是很难发现的 没什么别的了。

我的猜测/希望开发工具能够帮助解决这个问题,通过简单地通过视觉(不同的颜色,更强烈的字体等)来容易地识别不可变类型。但我认为这就是解决办法改变所有的方法名。