Какая польза от System.String.Copy в .NET? - PullRequest
63 голосов
/ 06 февраля 2009

Боюсь, это очень глупый вопрос, но я, должно быть, что-то упустил.

Почему можно использовать String.Copy (string) ?

В документации сказано, что метод

Создает новый экземпляр String с то же значение, что и указанная строка.

Поскольку строки неизменяемы в .NET, я не уверен, в чем выгода использования этого метода, так как я думаю, что

 string copy = String.Copy(otherString);

может показаться для всех практических целей таким же результатом, как

 string copy = otherString;

То есть, за исключением того, что происходит внутренняя бухгалтерия и тот факт, что копирование не ReferenceEquals для otherString, заметных отличий нет - String является неизменным классом, равенство которого основано на значении, а не на идентичности. (Спасибо @Andrew Hare за то, что он указал, что моя первоначальная формулировка была недостаточно точной, чтобы указать, что я понял, что существует разница между Copy и нет, но был обеспокоен ощущаемым отсутствием полезной разницы .)

Конечно, когда передан аргумент null, Copy выдает ArgumentNullException, и «новый экземпляр» может занять больше памяти. Последнее вряд ли кажется выгодным, и я не уверен, что нулевая проверка является достаточно большим бонусом, чтобы оправдать целый метод Copy.

Спасибо.

Ответы [ 8 ]

40 голосов
/ 06 февраля 2009

С String.Copy вы фактически выделяете новую память и копируете символы из одной строки в другую; вы получаете совершенно новый экземпляр, в отличие от того, чтобы обе переменные были одним и тем же экземпляром. Это может иметь значение, если вы используете строку с неуправляемым кодом, который напрямую связан с ячейками памяти и может изменить строку.

20 голосов
/ 06 февраля 2009

Вот одна часть головоломки. Это не объясняет, почему вы хотели бы сделать это, но это помогает объяснить функциональную разницу.

Если вы закрепите строку, используя ключевое слово 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
20 голосов
/ 06 февраля 2009

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, которая имеет то же содержание , но как другой объект в куче.


Редактировать: Многие люди очень расстроены, что я не ответил на вопрос ОП. Я считаю, что я ответил на вопрос, исправив неверную предпосылку в самом вопросе. Вот аналогичный (если не упрощенный) вопрос и ответ, который, я надеюсь, проиллюстрирует мою точку зрения:

Вопрос:

Я заметил, что у моей машины две двери, по одной на каждой стороне машины. Я считаю, что это правда, что независимо от того, какую дверь я использую, я в конечном итоге сижу на водительском сиденье. Какова цель другой двери?

Ответ:

На самом деле это неправда, что если вы используете любую дверь, вы окажетесь на сиденье водителя. Если вы используете боковую дверь водителя, вы окажетесь на сиденье водителя, а если вы используете боковую дверь пассажира, то окажетесь на сиденье пассажира.

Теперь в этом примере вы можете утверждать, что ответ на самом деле не является ответом, поскольку вопрос был "какова цель боковой двери пассажира?". Но так как этот вопрос был полностью основан на неправильном представлении о том, как работали двери, не следует ли из этого, что опровержение предпосылки прольет новый свет на назначение другой двери путем дедукции?

9 голосов
/ 03 июля 2011

Быстрый поиск в BCL для .NET 4.0 показывает, что метод string.Copy вызывается примерно в полудюжине мест. Обычаи делятся примерно на следующие категории:

  1. Для взаимодействия с собственными функциями, которые могут повредить передаваемые им строки. Если вы не можете повлиять на объявление P / Invoke и не можете исправить вызываемую функцию, string.Copy - лучший выбор.

  2. Для ситуаций, когда строка модифицируется на месте по соображениям производительности. Если вам нужно преобразовать всего несколько символов в нижний регистр в потенциально длинной строке, единственный способ сделать это без двухкратного копирования строки и создания дополнительного мусора - это изменить его.

  3. В местах, где это не кажется необходимым. Вполне возможно, что некоторые программисты больше привыкли к строкам Java или C ++ и не понимают, что копирование строки в C # редко бывает полезным.

8 голосов
/ 06 февраля 2009
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 указывает на буфер размером 100, который создал StringBuilder. (Посмотрите на метод StringBuilder.ToString())


Я думаю, StringBuilder использует String.Copy() и будучи частью .NET Framework StringBuilder действительно меняет содержимое string. Так что string не всегда неизменен.

1 голос
/ 06 февраля 2009

В дополнение к тому, что сказал 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 голосов
/ 06 февраля 2009

Я не уверен, как String внедряется в .NET, но я думаю, что Java - хороший справочник.

В Java новая String (str) также делает то, что String.copy (str); сделать, выделить новую строку с тем же значением.

Это кажется бесполезным, но очень полезно для оптимизации памяти.

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 выделять свой собственный символ [], чтобы предотвратить утечку / потерю скрытой памяти.

0 голосов
/ 06 февраля 2009
string a = "test";
string b = a;
//Object.ReferenceEquals(a,b) is true
a += "more";
//Object.ReferenceEquals(a,b) is now false !

автоматическое обнаружение изменений?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...