С#: Строки с одинаковым содержимым

Я слышал и читал, что строка не может быть изменена (неизменна?). Это должно быть правильно, я думаю. Но я также слышал, что две строки с одним и тем же содержимым имеют одно и то же пространство памяти (или то, что вы называете). Правильно ли это?

И если это так, означает ли это, что если я создам список с тысячами строк, на самом деле это не займет много места, если большинство этих строк были равны друг другу?

Ответы

Ответ 1

РЕДАКТИРОВАТЬ: В ответе ниже я упомянул о том, что внутренний пул является специфичным для AppDomain; Я почти уверен, что то, что я наблюдал раньше, но документы MSDN для String.Intern предполагают, что существует один общий пул для всего процесс, делая это еще более важным.

Оригинальный ответ

(я собирался добавить это как комментарий, но я думаю, что это достаточно важный момент для получения дополнительного ответа...)

Как объяснили другие, интернирование строк выполняется для всех строковых литералов, но не для "динамически созданных" строк (например, те, которые читаются из базы данных или файла или построены с использованием StringBuilder или String.Format.)

Однако я бы не предложил называть String.Intern, чтобы обойти последний пункт: он будет заполнять интервал для жизни вашего AppDomain. Вместо этого используйте пул, который является локальным только для вашего использования. Вот пример такого пула:

public class StringPool
{
    private readonly Dictionary<string,string> contents =
        new Dictionary<string,string>();

    public string Add(string item)
    {
        string ret;
        if (!contents.TryGetValue(item, out ret))
        {
            contents[item] = item;
            ret = item;
        }
        return ret;
    }
}

Тогда вы просто используете что-то вроде:

string data = pool.Add(ReadItemFromDatabase());

(Обратите внимание, что пул не является потокобезопасным, нормальное использование ему не понадобится.)

Таким образом вы можете выбросить свой пул, как только он вам больше не понадобится, вместо того, чтобы навсегда сохранить потенциально большое количество строк. Вы также могли бы сделать это умнее, внедрить кеш LRU или что-то еще, если вы действительно захотите.

EDIT: просто для того, чтобы уточнить, почему это лучше, чем использовать String.Intern... предположим, что вы читаете кучу строк из базы данных или файла журнала, обрабатываете их, а затем переходите на другую задачу. Если вы вызываете String.Intern в этих строках, они никогда не будут собираться мусором, пока ваш AppDomain жив, и, возможно, даже не тогда. Если вы загружаете несколько разных файлов журналов, вы постепенно накапливаете строки в своем пуле, пока не закончите или не закончите работу. Вместо этого я предлагаю такой шаблон:

void ProcessLogFile(string file)
{
    StringPool pool = new StringPool();
    // Process the log file using strings in the pool
} // The pool can now be garbage collected

Здесь вы получаете преимущество нескольких строк в том же файле, что только один раз в памяти (или, по крайней мере, только один раз пропустил gen0 один раз), но вы не загрязняете "глобальный" ресурс (пул пула).

Ответ 2

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

http://msmvps.com/blogs/manoj/archive/2004/01/09/1549.aspx

Ответ 3

Если я правильно помню, строка, жестко закодированная в коде, объединяется отдельно. Это называется "Интернетом", и существует способ запроса, является ли строка: String.IsInterned Method

На этой странице в разделе "Замечания" вы можете прочитать:

Среда общего языка автоматически поддерживает таблицу, называемую "пулом-пул", которая содержит один экземпляр каждой уникальной строковой константы литерала, объявленной в программе, а также любой уникальный экземпляр String, который вы добавляете программно.

Надеюсь, это поможет вам немного, и исправьте меня, если я ошибаюсь.

Маттиас

Ответ 4

Чтобы заставить строки "делиться" своими ячейками памяти, нужно ставить их в пул-пул, который содержит единственную ссылку на каждую уникальную литеральную строку, объявленную или создаваемую программно в вашей программе.

Обратите внимание, что все строковые литералы в коде автоматически интернированы.