【问题标题】:Does an immutable list that overloads '+' makes sense?重载“+”的不可变列表有意义吗?
【发布时间】:2010-08-07 00:32:48
【问题描述】:

它当然不会脱离 .NET 框架的标准实践。当我看到a + b 时,我总是假设会创建一些新的东西。

static void Main(string[] args)
{
    var list = BuildList(ImmutableList<int>.Empty);
    var sum = (list + 500).Sum();
    Console.WriteLine(sum);
    Console.ReadLine();
}

static ImmutableList<int> BuildList(ImmutableList<int> list)
{
    if (list.Count < 1000)
    {
        return BuildList(list + list.Count);
    }
    return list;
}

更新

请参阅 Jon Skeet 的帖子,了解 name methods on immutable lists 的内容。

令人惊讶的反应

看到这么多答案都同意这是有道理的,我感到非常惊讶。原则上我也同意,但是对于我来说,阅读冗长的代码要比那些不习惯阅读简洁的代码的人更容易阅读。从我的工作经验来看,他们似乎是大多数。

【问题讨论】:

  • 当然,这基本上是 Haskell 的列表理解。没有错。

标签: c# operator-overloading immutability


【解决方案1】:

我个人不建议将+ 之类的运算符重载用于任何不应被视为原语的类。特别是,在我看来,集合永远不应该重载运算符。

当您有一个小的不可变类或结构,其行为类似于“原始”类型时,运算符重载是有意义的。但是,当您开始尝试对其他类执行此操作时,它确实会影响可维护性。

我建议改为进行显式方法调用。


阅读完 cmets 后,我的建议是使用 Concat()(匹配框架中的 Enumerable.Concat)。我更喜欢的另一个选项是Construct(),ala cons(尽管通常会在前面加上 cons),以使用法非常清楚:

var list = BuildList(ImmutableList<int>.Empty);
var list2 = list.Concat(500); 
// Or: var list2 = list.Construct(500);

【讨论】:

  • 这是一个 struct,它不能以任何方式发生变异,所以本质上它的行为与 int 完全一样。
  • @ChaosPandion:不,真的没有。它像 int 一样不可变,但在概念上非常不同。集合在概念上不是原始对象。
  • @ChaosPandion:我并不是说你~不能~这样做——只是我觉得它在你的 API 中添加了一些非常不清楚的东西,并且可能会显着降低你的代码的可维护性和可理解性.
  • +1。 Reed 的意见符合 Microsoft 的框架设计指南。
  • @ChaosPandion:这个方法会返回什么?如果你做list = new IL([] {1,2,3}); list2 = list.Foo(500);,list2 会包含什么?
【解决方案2】:

重载+ 的不可变字符列表是有意义的;我们称它们为字符串。以此类推,您可以说一个不可变的列表,其中包含重载 + 运算符以调用 Concat 操作的任何内容。

顺便说一句,我最近创建了一个与您描述的类似的功能:

public static IEnumerable<T> Append<T>(this IEnumerable<T> list, T item)
{
    foreach (T i in list)
        yield return i;
    yield return item;
}

我决定Append 将单个元素添加到列表中,而Concat 将整个列表添加到列表末尾。

【讨论】:

  • +1 - 你知道这就是我的想法,但这似乎引起人们的困惑真的超过了使用它的简洁性。现在我永远不会在我的个人项目之外使用它,那么我可能会使用它,但我也想在我的工作中使用它。 (我厌倦了可以为空的可变列表引起的问题。)
  • 也许我并不感到困惑,因为 Python 使用+ 进行列表连接,但我记得我第一次看到它时并没有感到困惑。什么样的人会被list + list迷惑?
  • 因为有些人不思考
  • 那是完美的。我有我的界面。 :)
【解决方案3】:

+ 在这种情况下是什么意思?我似乎真的不清楚(没有阅读代码)。 IList + IList 我可以将其视为联合运算符或组合列表(保留重复项)或任何数量的其他事物。 IList + int 让我很困惑。在我看来,一个具有清晰名称的方法将产生比某人必须查找的运算符或每次使用重载运算符时的 cmets 更具可读性的代码。

【讨论】:

  • 你会给这个方法起什么名字?
  • 假设这会将一个元素添加到列表中,那么您提供给 Jon Skeet 帖子的链接是一个很好的资源。我个人可能会将我的帽子作为对.Cons().Add() 的投票——除了可变性之外,这与List 上的.Add() 相同。
  • 我在ConsConcat 之间徘徊。
  • cons 操作将一个新元素放在列表的头部; concat 操作创建一个新列表,其中包含第一个列表的所有元素,然后是第二个列表的所有元素。它们是根本不同的操作。
【解决方案4】:

关于运营商的一件事是,人们期望他们相对高效。没有什么可以说这应该成立,但人们似乎确实期待它。

如果在内部,这种列表的结构允许您创建一个串联列表,方法是让它存储对它的来源的两个的引用(很有可能,特别是考虑到不变性消除了混淆副作用的风险),然后我会去的。如果它可能不得不进行相对昂贵的操作,那么我不会。 “concatenate()”可能会让某人考虑“我真的需要在我目前所处的这个紧密循环中移动这么多内存,还是应该考虑一种不同的方法”,而此时这种想法是合适的。

【讨论】:

  • 我进行了一些性能测试,我的实现出奇地好,结果类似于string。显然没有那么好,但很相似。
  • 虽然,“知道 + 对字符串很慢”通常是告诉新手的第一件事,正是因为他们可能会假设其他情况(然后它被夸大到过度避免它的地步,但那是另一回事)。以这样的速度,我想我会反对,但我也不认为有人这样做是错的。
  • 在需要紧密循环的情况下。我会说使用可变列表,但将其范围限定为单个函数。
  • 同意,但请记住,+ 和 Concat 之间最终没有代码区别,区别仅在于阅读和编写代码的人的头脑中。因此,其效果是鼓励或阻止不同的方法;这是关于一个人在一个紧密的循环中做什么的心理问题,而不是他们应该做什么的实际问题,我们希望心理学和实际问题相匹配。
【解决方案5】:

我愿意。

毕竟,字符串是一个字符列表。

是的,如果我看到 + 我会立即想到连接(唯一的另一种可能性是向量和,这种情况很少见)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-23
    • 1970-01-01
    • 1970-01-01
    • 2019-06-27
    • 1970-01-01
    • 2014-08-09
    • 1970-01-01
    相关资源
    最近更新 更多