存储一个字符串的引用

3

我有一个类,其中有3个字符串属性。我想将它们存储在列表中,以便当我更改列表中的字符串时,它们也会在类中更新。

如果我使用类对象,这将很容易实现,但是字符串似乎表现不同。它似乎为列表制作了对象的副本,而不是指向对象的指针。我应该如何在C#中做到这一点?如果这不可能,是否有更好的方法?

9个回答

6
< p > string 的问题在于它们是不可变的。换句话说,一旦创建了一个 string,就不能再更改它。

因此,如果你想要“更改”一个 string,你必须从 List 中删除原始字符串,并将结果存储回列表中。例如:

string a = "abcdefg";
List<String> list = new List<String>();
list.add(a);

a = a.Substring(0, 5);

那段代码并没有实际作用,因为 string a 所指向的内容从来没有发生过变化。它只是指向了一个新的 string

3

字符串是不可变的。你可以改变一个引用来指向另一个字符串,但你不能修改一个字符串使其它引用的值也随之改变(除非使用不安全、完全危险的反射代码)。

你想要做的是通过使用可变的替代方案(如StringBuilder),或通过显式间接处理来处理这个问题。我将展示后者:

public class Props
{
    private readonly string[] data = new string[2];

    public string Foo {
        get { return data[0]; }
    }

    public string Bar {
        get { return data[1]; }
    }

    public IList<string> ModifyValueButNoInsertsList { get { return data;} }
}

在这种情况下,您应该考虑实际使用 string[] 而不是 IList,因为它清晰地表明插入是被禁止的,只有对值进行修改。由于 string[] 实现了 IList<string>,因此这不太可能成为问题。


3
在.NET中,字符串是不可变的。如果你改变了字符串,实际上是创建了一个新的字符串并修改了引用。
我建议使用StringBuilder对象来解决你的问题。

+1. StringBuilder已经实现了你想要的功能,除非现有的类无法使用,否则不要创建一个新类。 - Dour High Arch
StringBuilder 可以使用,但从性能的角度来看是一个糟糕的选择。更改实例值的唯一方法是清除它并附加新字符串,这实际上涉及复制所有这些字节。如果您查看内部结构,您会发现在第一次调用 ToString 时它会进行第二次复制。此外,由于更改它涉及的不仅仅是重置指针,因此可能存在线程安全的问题。 - Steven Sudit
@Steven,请提供一些证据支持这个说法。我的StringBuilder测试只显示更改/替换/附加的字节发生变化,而不是“所有这些字节”。很少有字符串操作仅涉及“更改指针”;其他建议的可变字符串类也没有这样做,它们也不是原子性的。所有其他建议都涉及创建一个新字符串。 - Dour High Arch
@Dour,如果你使用Reflector查看,你会发现它使用一个字符串作为缓冲区,所以当你将另一个字符串赋值给它时,它实际上必须复制源中的所有字节。(它还必须在调整大小时重新复制它们,但我非常确定它使用了一种倍增算法。但是如果你使用ToString然后添加,它会再次复制。)另一方面,将不可变字符串分配给数组的零元素只是指针操作,它可以是原子的。 - Steven Sudit

2

C#中的字符串是不可变的,因此您无法更改C#中的字符串 - 您只能创建新字符串。

相反,您可以存储具有字符串成员的类。

class StringHolder {
  public StringHolder(string s) { str = s;} 
  public string str;
}
...
List<StringHolder> l1 = new List<StringHolder>();
List<StringHolder> l2 = new List<StringHolder>();
List<StringHolder> l3 = new List<StringHolder>();

StringHolder h = new StringHolder("Test\n");
l1.add(h);
l2.add(h);
l3.add(h);

h.str = h.str.Replace("\n","");

现在所有的列表都引用同一个StringHolder,因此自然会看到相同的字符串。

另一种选择是将StringBuilder对象存储在列表中,而不是一个String。


2
class StringHolder
{
    public string Value { get; set; }
}

不要只保留字符串,而是保留一个列表。这样,您就可以获取/设置Value属性以更新字符串值。


2

由于字符串是不可变的,最简单的解决方法是存储一个只有一个元素的字符串数组的引用。然后,任何引用该数组的人都会注意到替换该元素。


0

你正在寻找一种可变字符串。有很多方法可以创建一个行为符合你要求的类。

最简单的方法是使用 StringBuilder 对象而不是字符串。你只需要小心不要创建新的 StringBuilder 对象,而是修改现有的对象。根据你的需求,这可能不是最佳选择。

另一种方法是创建自己的 String 包装类,可以自由地操纵它。缺点是你可能需要编写许多存根方法,这些方法调用内部字符串,具体取决于你想如何使用它。更容易的方法是公开一个读/写字符串属性。这样做的好处是可以定义你想要的行为,但在第一次编写时需要花费更长的时间。同样,你必须确保不创建包装类的新实例,而是仅修改类的内部字符串。


0

将您的字符串包装到自定义类中,这样可以让您在多个不同的位置之间共享它。您也可以选择存储字符数组。


如果你只需要一个间接层,我认为自定义类就有点杀鸡用牛刀了。 - Steven Sudit

0
作为一个旁注(就像其他人提到的那样),如果你正在使用字符串进行一些重度处理,请使用StringBuilder类。由于字符串的不可变性质,在循环中更改/连接它们等操作会导致很多开销。
StringBuilder是你的好朋友。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接