79

恐怕这是一个非常愚蠢的问题,但我一定遗漏了一些东西。

为什么要使用String.Copy(string)

文档说方法

创建一个新的 String 实例,其值与指定的 String 相同。

由于字符串在 .NET 中是不可变的,所以我不确定使用这种方法有什么好处,因为我认为

 string copy = String.Copy(otherString);

出于所有实际目的,似乎会产生与

 string copy = otherString;

也就是说,除了正在进行的任何内部簿记以及复制不是ReferenceEquals到 otherString 的事实之外,没有可观察到的差异 - String 是一个不可变类,其相等性基于值,而不是身份。(感谢@Andrew Hare 指出我最初的措辞不够精确,无法表明我意识到Copying 和 not 之间存在差异,但担心缺乏有用的差异。)

当然,当传递一个null参数时, Copy 会抛出一个ArgumentNullException,并且“新实例”可能会消耗更多内存。后者似乎几乎没有好处,而且我不确定 null 检查是否足以保证整个 Copy 方法的好处。

谢谢。

4

9 回答 9

51

String.Copy您实际上是在分配新内存并将字符从一个字符串复制到另一个字符串;你得到一个全新的实例,而不是让两个变量都是同一个实例。如果您将字符串与直接处理内存位置并可以改变字符串的非托管代码一起使用,这可能很重要。

于 2009-02-06T16:18:46.950 回答
26

这是拼图的一部分。它没有解释你为什么要这样做,但它确实有助于解释功能差异。

如果您使用fixed关键字固定字符串,则内容将是可变的。在我的脑海中,我想不出你会想要这样做的情况,但这是可能的。

string original = "Hello World";
string refCopy = original;
string deepCopy = String.Copy(original);

fixed(char* pStr = original)
{
   *pStr = 'J';
}

Console.WriteLine(original);
Console.WriteLine(refCopy);
Console.WriteLine(deepCopy);

输出:

Jello World
Jello World
Hello World
于 2009-02-06T19:16:56.713 回答
21

String.Copy返回一个新的String并且不会产生相同的结果

String copy = otherString;

试试这个:

using System;

class Program
{
    static void Main()
    {
        String test = "test";
        String test2 = test;
        String test3 = String.Copy(test);

        Console.WriteLine(Object.ReferenceEquals(test, test2));
        Console.WriteLine(Object.ReferenceEquals(test, test3));

        Console.ReadLine();
    }
}

当您设置test2 = test这些引用指向相同的String. 该Copy函数返回一个新String引用,该引用具有相同的内容,但作为堆上的不同对象。


编辑:有很多人对我没有回答 OP 的问题感到非常沮丧。我相信我确实通过纠正问题本身的不正确前提来回答这个问题。这是一个类似的(如果不是过于简单的话)问题和答案,希望能说明我的观点:

问题:

我观察到我的车有两扇门,每侧一扇。我相信这是真的,无论我使用哪扇门,我最终都会坐在驾驶座上。另一扇门的目的是什么?

回答:

实际上,如果您使用任何一扇门,您最终会坐在驾驶座上,这是不正确的。如果您使用驾驶员侧门,您最终将坐在驾驶员座位上,如果您使用乘客侧门,您将最终坐在乘客座位上。

现在在这个例子中,你可以争辩说答案并不是真正的答案,因为问题是“乘客侧门的目的是什么?”。但是,既然这个问题完全是基于对门是如何工作的误解,难道不就意味着对前提的反驳将通过演绎揭示另一扇门的目的吗?

于 2009-02-06T16:16:16.267 回答
9

通过 BCL 快速搜索 .NET 4.0 显示该string.Copy方法在大约六处被调用。用法大致分为以下几类:

  1. 用于与可能损坏传递给它们的字符串的本机函数的互操作。如果你不能影响 P/Invoke 声明并且你不能修复被调用的函数,string.Copy是最好的选择。

  2. 对于出于性能原因而就地修改字符串的情况。如果您只需要将一个可能很长的字符串中的几个字符转换为小写,那么在不复制字符串两次并创建额外垃圾的情况下,唯一的方法就是对其进行变异。

  3. 在似乎没有必要的地方。很可能有些程序员更习惯于 Java 或 C++ 字符串,而没有意识到在 C# 中复制字符串很少有用。

于 2011-07-03T05:01:25.693 回答
8
string a = "abc";
string b = String.Copy(a);

Monitor.Enter(a); // not the same as Monitor.Enter(b);

然而

string c = "123";
string d = c;
Monitor.Enter(c); // the same as Monitor.Enter(d);

至于任何人都会关心的方式,我认为这是为了完整性。


StringBuilder sb = new StringBuilder(100);
sb.Append("abc");
string a = sb.ToString();
string b = String.Copy(a);

我认为a会占用更多的 RAM b,因为a指向StringBuilder创建的大小为 100 的缓冲区。(看里面的StringBuilder.ToString()方法)


我认为StringBuilder使用String.Copy()并成为 .NET 框架的一部分StringBuilder确实会改变 .NET 框架的内容string。所以 astring并不总是不可变的。

于 2009-02-06T16:26:44.827 回答
3

10 多年后访问此主题,使用 NET 5,请注意String.Copy自 NET Core 3.0 以来已过时。

所以很好,为什么使用这个问题的答案String.Copy现在可能是:请不要!

请参阅MS 文档

Microsoft 文档中的 Doc 警告

于 2021-01-13T05:30:12.333 回答
1

除了 tvanfosson 所说的(我认为您不能从非托管代码访问托管字符串使用的缓冲区......我知道这至少会很困难),我相信如果字符串是用作对多线程功能进行锁定的对象。

例如...

using System;

public class Class1
{
    string example1 = "example";
    string example2 = example1;

    public void ExampleMethod1()
    {
        lock (example1)
        {
            Console.WriteLine("Locked example 1");
            //do stuff...
        }
    }

    public void ExampleMethod2()
    {
        lock (example2)
        {
            Console.WriteLine("Locked example 2");
            //do stuff
        }
    }
}

我相信如果这两个示例方法并行运行,它们将锁定同一个对象,因此当另一个在其锁定块内时,一个将无法执行。

但是,如果您将其更改为此...

using System;

public class Class1
{
    string example1 = "example";
    string example2 = string.Copy(example1);

    public void ExampleMethod1()
    {
        lock (example1)
        {
            Console.WriteLine("Locked example 1");
            //do stuff...
        }
    }

    public void ExampleMethod2()
    {
        lock (example2)
        {
            Console.WriteLine("Locked example 2");
            //do stuff
        }
    }
}

然后我相信它们只会阻止执行相同方法的其他线程的执行(即任何执行 ExampleMethod1 的线程都将被锁定,直到每个线程完成,但它们不会干扰运行 ExampleMethod2 的线程)。

不确定这是一个有用的区别,因为有更好的同步机制(我不认为锁定字符串是一个好主意)。

于 2009-02-06T18:32:32.203 回答
0
string a = "test";
string b = a;
//Object.ReferenceEquals(a,b) is true
a += "more";
//Object.ReferenceEquals(a,b) is now false !

自动变化检测 ?

于 2009-02-06T16:50:03.687 回答
-1

我不确定 String 是如何在 .NET 中实现的,但我认为 Java 是一个很好的参考。

在 Java 中, new String(str) 还可以做什么 String.copy(str); 做,分配一个具有相同值的新字符串。

看起来没什么用,但在内存优化方面非常有用。

String 在实现中包含一个带有偏移量和长度的 char[]。如果您执行子字符串之类的操作,它不会执行内存复制,而是返回一个共享相同 char[] 的新字符串实例。在很多情况下,这种模式会节省大量的内存复制和分配。但是,如果你在一个长的大字符串中子串一个小片段。即使原始的大字符串能够被GC,它仍然会引用大字符[]。

String longString = // read 1MB text from a text file
String memoryLeak = largeString.substring(100,102); 
largeString=null;
// memoryLeak will be sized 1MB in the memory
String smaller = new String(largeString.substring(100,102));
// smaller will be only few bytes in the memory

它可以强制新的 String 对象分配它自己的 char[] 以防止隐藏的内存泄漏/浪费。

于 2009-02-06T18:02:48.393 回答