【问题标题】:How to get a list of mutable strings?如何获取可变字符串列表?
【发布时间】:2011-08-02 14:10:09
【问题描述】:

我有以下代码

  List<String> l = new List<String>();
  String s = "hello";
  l.Add(s);
  s = "world";

当我设置一些断点并通过程序时,执行完最后一行后,列表中的值仍然是hello而不是world

不应该等于 world 吗?字符串不是对象吗,我不只是将指针插入列表吗?稍后如果我将字符串更改为指向不同的值(“世界”),为什么我的列表仍然引用旧值?

我怎样才能达到我想要的效果? 非常感谢!

【问题讨论】:

    标签: c# string reference


    【解决方案1】:

    字符串是不可变的,所以它不起作用。当您尝试设置它时,您实际上将指针放到旧字符串并在引擎盖下创建一个新字符串。

    为了得到想要的效果,创建一个包装字符串的类:

    public class SortOfMutableString
    {
        public string Value {get;set;}
    
        public SortOfMutableString(string s)
        { 
            Value = s;
        }
    
        public static implicit operator string(SortOfMutableString s)
        {
            return s.Value;
        }
    
        public static implicit operator SortOfMutableString(string s)
        {
            return new SortOfMutableString(s);
        }
    }
    

    并在您的列表中使用它。然后引用将指向该类,但您可以在里面包含字符串值。为了让它变得更好,覆盖implicit 与字符串之间的转换,这样您甚至不需要看到您正在与SortOfMutableString 交谈。

    请参阅 Jon Skeet 的回答,无疑对 C# 中的字符串有非常准确的解释,我什至不会打扰!

    替代类名:

    • 伪可变字符串
    • ICantBelieveItsNotMutable
    • HappyAndReferenceableString

    【讨论】:

    • 我个人很喜欢 ICantBelieveItsNotMutable,你需要 Fabio 做你的代言人。
    • 谢谢,类名很好 :P 任何方式我都可以覆盖赋值 (=) 运算符,以便在 s = "world" 上它分配给主类中的 value 属性?
    • 不是等号运算符,但如果你像我上面那样覆盖隐式,它会达到同样的效果。
    • 我所说的相等,是指赋值运算符! :-)
    【解决方案2】:

    您正在更改 s 引用以引用不同的 String 实例。
    字符串是不可变的;无法更改您添加到列表中的现有实例。

    相反,您可以创建一个具有可写 String 属性的可变 StringHolder 类。

    【讨论】:

      【解决方案3】:

      不,它不应该等于 world。变量s 的值是一个引用。当您调用l.Add(s) 时,该引用将按值 传递给列表。所以列表现在包含对字符串“hello”的引用。

      您现在将s 的值更改为对字符串“world”的引用。这根本不会改变列表。

      区分三个截然不同的概念很重要:

      • 变量(具有名称和值)
      • 引用(允许您导航到对象的值,或null
      • 一个对象

      所以特别是,列表不知道任何关于 变量 s 的信息 - 它知道传递给 Add;在调用Add 时,该值发生s 的值,仅此而已。

      您可能会发现这些文章很有帮助:

      【讨论】:

      • 谢谢。所以我可以用任何方式做这样的事情,但通过引用而不是值传递?
      • @Rafal:不——当方法调用完成时,ref-ness 将有效地过期。您不能在方法调用范围之外保留另一个变量的句柄。
      【解决方案4】:

      不,涉及两个不同的引用。一个叫s,一个叫List[0]。当您说l.Add(s) 时,您将列表引用设置为与s 相同的地址,但是当您将s 分配给“世界”时,s 将指向新字符串,留下List[0] 指向到旧字符串。

      如果您真的想做您所要求的事情,您需要将字符串包装在另一个包含字符串的对象中,以便 sList[0] 都引用该对象,然后对象对字符串的引用可以改变,两者都会看到。

       public class StringWrapper
       {
           public string TheString { get; set; }
       }
      

      那么你可以这样做:

       var s = new StringWrapper { TheString = "Hello" };
       var l = new List<StringWrapper>();
       l.Add(s);
       s.TheString = "World";
      

      现在l[0].TheString 也将成为世界。这是可行的,因为在这种情况下,我们不会更改 List[0] 或 s 中的引用,而是更改 s 和 List[0] 引用的对象的内容。

      【讨论】:

        【解决方案5】:

        变量是对象引用,而不是对象本身。 s = "world" 说“使s 引用字符串"World") - 它不会以任何方式影响s 之前引用的字符串"hello"。此外,C# 中的字符串始终是不可变的。您可以,但是,使第一个列表元素(当前引用 "hello")引用不同的字符串:l[0] = "world"

        【讨论】:

          【解决方案6】:

          这里的其他两个答案很好地说明了为什么您尝试的方法不起作用,但是您正在寻找一种解决方案来达到您想要的效果。在对象内包装字符串(属性)。然后您可以更改该字符串,它将反映在集合中。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-10-23
            • 1970-01-01
            • 2015-04-24
            • 2012-05-03
            • 2019-02-21
            • 2011-06-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多