.NET中System.String.Copy的用途是什么?


71

恐怕这是一个非常愚蠢的问题,但是我必须缺少一些东西。

为什么要使用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方法。

谢谢。


1
好吧,愚蠢的问题有很多人投票赞成,
TarunG

Answers:


45

随着String.Copy你实际上是在分配新的内存,然后复制一个到另一个字符串中的字符; 您将获得一个全新的实例,而不是将两个变量都设为同一个实例。如果将字符串与非托管代码一起使用可能会很重要,该托管代码直接处理内存位置并可以使字符串发生变异。


2
虽然这是我想到的第一件事,但肯定听起来像是一个等待发生的错误-如果您正在与更改字符串的非托管代码执行互操作,则可能应该使用StringBuilder。
迈克尔·伯

4
如果您尝试修改不可变的对象,这听起来像一个错误模式。
丹尼斯C,2009年

1
我从未说过我会这样做,而且我想不出有什么例子会吸引我这样做。另一方面,如果我需要使用处理字符串的非托管代码,并且需要对其进行修改,那么我可能会复制我的字符串并将其传递以避免错误。
tvanfosson,2009年

1
是的,那是我所缺少的部分-当您将内存分配给外部代码时会发生什么。在工作中,我们将字符串作为不可修改的参数传递给非托管代码,但是当我们希望事情发生混乱时,请使用StringBuffers -我不会想到使用String来实现。谢谢。
布莱尔·康拉德

3
我想说这个答案意味着String.Copy没有理智的用处!
Daniel Earwicker 09年

25

这是一个难题。它没有说明您为什么要这样做,但确实有助于说明功能上的差异。

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

1
那是可怕的和可怕的。范例为+1。
Patrick M

很棒的答案。
Kalyan

它不值得冒犯,但通常可能会被滥用。例如,出于安全原因,在处理SecureString或将敏感数据短暂存储在内存中时,您可能希望擦除字符串的内存内容,而不是等待垃圾回收。如果不再需要时立即清除内存,则转储内存内容和捕获仅等待清理的数据将变得更加困难。
迈克尔·布朗

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的问题。我相信我确实通过更正问题本身的不正确前提来回答问题。这是一个类似的问题(如果没有简化的话),希望可以说明我的观点:

题:

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

回答:

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

现在,在此示例中,您可能会争辩说答案不是真正的答案,因为问题是“乘客侧门的目的是什么?”。但是,由于这个问题完全基于对门的工作方式的误解,难道不就这样来了:前提的反驳将通过演绎为另一扇门的目的提供新的启示?


问题不是“它做什么”,而是“我为什么要使用它?”
彼得·莫里斯

我认为重点是:“复制不可变信息有什么用?” 而不是重复使用它。
PhiLho

整个问题基于错误的前提,即“字符串一= String.Copy(two);”。和“字符串一=二;” 是等效的语句。我只是指出海报是错误的。
Andrew Hare

OP是“为什么要使用String.Copy(string)?” OP意识到它可以进行复制,但是鉴于.Net字符串是不可变的,这有什么用?
abelenky

如果OP意识到问题为何包含以下语句:“字符串复制= String.Copy(otherString);会产生与字符串复制= otherString;相同的结果”?
安德鲁·黑尔

9

在.NET 4.0的BCL中进行的快速搜索显示,该string.Copy方法在大约六个地方被调用。用法大致分为以下几类:

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

  2. 对于出于性能原因就地修改了字符串的情况。如果您只需要在可能很长的字符串中将几个字符转换为小写,则唯一的方法是不对字符串进行两次复制和创建其他垃圾的操作,就是对它进行突变。

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


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并不总是不变的。


StringBuilder不必修改字符串。在内部,它可以使用List <char>来有效地增加大小,然后ToString方法将从列表的内容中分配一个字符串。
Daniel Earwicker 09年

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的线程)。

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


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

自动变化检测


3
不,附加到字符串会创建一个新的字符串实例(因为字符串是不可变的)。附加“ a”后将指向另一个实例。
e11s

-1

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

在Java中,新的String(str)也可以执行String.copy(str); 这样做,分配一个具有相同值的新String。

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

字符串包含char [],其实现中具有偏移量和长度。如果您执行类似子字符串的操作,则不会进行内存复制,而是返回共享相同char []的新String实例。在许多情况下,此模式将节省大量内存复制和分配。但是,如果在长而大的String中将一小段子字符串化。即使原始的大String可以是GC,它仍将引用大char []。

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 []以防止隐藏的内存泄漏/浪费。


这不是它的工作方式。仅仅假设.NET具有与Java相同的实现也是不正确的。最重要的是,答案与问题无关。当您.Substring(100,102):会出现一个长度为102的新字符串,其中包含字符100 ... 201
Sander Rijken 2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.