在.NET中使用System.String.Copy有什么用?

时间:2009-02-06 16:07:20

标签: .net string copy immutability

我担心这是一个非常愚蠢的问题,但我必须遗漏一些东西。

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

文档说明了方法

  

使用创建String的新实例   与指定String相同的值。

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

 string copy = String.Copy(otherString);
出于所有实际目的,

似乎会产生与

相同的结果
 string copy = otherString;

也就是说,除了正在进行的任何内部簿记,以及副本不是ReferenceEquals到otherString的事实,没有可观察到的差异 - 字符串是一个不可变的类,其相等性基于值,而不是身份。 (感谢@Andrew Hare指出我的原始措辞不够精确,表明我意识到Copy之间存在差异,但却担心缺少有用的差异。)

当然,当传递null参数时,Copy会抛出ArgumentNullException,而“new instance”可能会占用更多内存。后者几乎不是一个好处,我不确定空检查是否足以保证整个复制方法。

感谢。

9 个答案:

答案 0 :(得分:40)

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

答案 1 :(得分:20)

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时,这些引用指向相同的StringCopy函数返回一个新的String引用,该引用具有相同的内容,但作为堆上的其他对象。


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

<强>问题:

  

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

<强>答案:

  

实际上,如果您使用任何一扇门,您将最终进入驾驶员座位。如果您使用驾驶员侧门,您将最终进入驾驶员座位,如果您使用乘客的侧门,您将最终进入乘客座位。

现在在这个例子中你可以说答案不是真的答案,因为问题是“乘客侧门的目的是什么?”。但是,既然这个问题完全是基于对门如何运作的误解,那么这个前提的反驳是否会通过演绎来揭示另一扇门的目的?

答案 2 :(得分:18)

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

如果使用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

答案 3 :(得分:9)

通过BCL for .NET 4.0的快速搜索显示,在大约六个地方调用了string.Copy方法。用法大致分为以下几类:

  1. 对于可能损坏传入其中的字符串的本机函数的互操作。如果您不能影响P / Invoke声明并且无法修复被调用的函数,string.Copy是最佳选择。

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

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

答案 4 :(得分: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会占用更多内存,然后占用b,因为a指向StringBuilder创建的大小为100的缓冲区。 (查看StringBuilder.ToString()方法的内部)


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

答案 5 :(得分:2)

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

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

MS Doc

Doc warning from Microsoft documentation

答案 6 :(得分: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的线程。)

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

答案 7 :(得分:0)

string a = "test";
string b = a;
//Object.ReferenceEquals(a,b) is true
a += "more";
//Object.ReferenceEquals(a,b) is now false !

自动更改检测?

答案 8 :(得分:-1)

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

在Java中,新的String(str)也做了String.copy(str); do,分配一个具有相同值的新String。

它似乎没用,但它在内存优化中非常有用。

String包含一个在实现中具有偏移量和长度的char []。 如果您执行类似子字符串的操作,则它不会执行内存复制,而是返回共享相同char []的新String实例。在许多情况下,这种模式将节省大量的内存复制和分配。但是,如果您在长大字符串中对一小段进行子串。它仍将引用大型char [],即使原始的大型String也可以是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 []以防止隐藏的内存泄漏/浪费。