Сегодня я заметил, что Строковый класс C# возвращает длину строки как Интервал. Так как Интервал всегда - 32 бита, независимо от того, что архитектура, это означает, что строка может только составить 2 ГБ или меньше в длине?
Строка на 2 ГБ была бы очень необычна, и представила бы много проблем наряду с ним. Однако большая часть API.NET, кажется, использует 'интервал' для передачи значений, таких как длина и количество. Это означает, что мы навсегда ограничены размерами набора, которые помещаются в 32 бита?
Походит на фундаментальную проблему с API.NET. Я ожидал бы, что вещи как количество и длина будут возвращены через эквивалент 'size_t'.
Похоже на фундаментальную проблему с .NET API ...
Не знаю, зашел бы я так далеко.
Рассмотрим практически любой класс коллекций в .NET. Скорее всего, у него есть свойство Count
, которое возвращает int
. Это говорит о том, что класс ограничен размером int.MaxValue
(2147483647). Это не проблема ; это ограничение - и совершенно разумное в подавляющем большинстве сценариев.
В любом случае, что было бы альтернативой? Есть uint
, но он не соответствует требованиям CLS. Тогда есть long
...
Что, если Length
вернет long
?
Попытайтесь представить себе ошеломляющую стоимость некоторого кода, подобного этому:
// Lord knows how many characters
string ulysses = GetUlyssesText();
// allocate an entirely new string of roughly equivalent size
string schmulysses = ulysses.Replace("Ulysses", "Schmulysses");
В принципе, если вы думаете о string
как о структуре данных, предназначенной для хранения неограниченного количества текста, вы: у меня нереалистичные ожидания. Когда дело доходит до объектов такого размера, возникает сомнение, есть ли у вас вообще необходимость хранить их в памяти (в отличие от жесткого диска).
Маловероятно, что вам понадобится хранить более двух миллиардов объектов в одной единственной коллекции. Вы столкнетесь с довольно серьезным снижением производительности при перечислении и поиске, которые являются двумя основными целями коллекций. Если вы имеете дело с таким большим набором данных, вы почти наверняка можете пойти по другому пути, например, разбить вашу единую коллекцию на множество меньших коллекций, которые содержат части всего набора данных, с которыми вы работаете.
Эй, подождите ... у нас уже есть эта концепция - это словарь !
Если вам нужно сохранить, скажем, 5 миллиардов английских строк, используйте этот тип:
Dictionary<string, List<string>> bigStringContainer;
Давайте сделаем так, чтобы строка ключа представляла, скажем, первые два символа строки. Затем напишите метод расширения, подобный этому:
public static string BigStringIndex(this string s)
{
return String.Concat(s[0], s[1]);
}
, а затем добавьте элементы в bigStringContainer следующим образом:
bigStringContainer[item.BigStringIndex()].Add(item);
и вызовите его в день.(Очевидно, есть более эффективные способы сделать это, но это всего лишь пример)
О, и если вам действительно действительно нужно иметь возможность искать любой произвольный объект по абсолютному индексу, используйте Массив
вместо коллекции. Хорошо, да, вы используете некоторую безопасность типов, но вы можете индексировать элементы массива с помощью long
.
Тот факт, что фреймворк использует Int32
для Count
/Length
свойств, индексаторов и т.д., является немного красной селедкой. Настоящая проблема заключается в том, что CLR в настоящее время имеет ограничение на максимальный размер объекта в 2 ГБ.
Поэтому строка
- или любой другой отдельный объект - никогда не может быть больше 2 ГБ.
Изменять свойство Length
типа string
на long
, ulong
или даже BigInteger
бессмысленно, так как в любом случае у вас никогда не может быть более 2^30 символов (максимальный размер 2GB и 2 байта на символ. )
Аналогично, из-за ограничения в 2 ГБ единственными массивами, которые могут даже приблизиться к 2^31 элементу, будут массивы bool[]
или byte[]
, которые используют только 1 байт на элемент.
Конечно, ничто не мешает вам создавать свои собственные составные типы, чтобы обойти ограничение в 2 ГБ.
(Обратите внимание, что приведенные выше замечания относятся к текущей реализации Microsoft и вполне могут измениться в будущих выпусках. Я не уверен, что Mono имеет подобные ограничения.)
Если вы работаете с файлом размером 2 ГБ, это означает, что вы, вероятно, будете использовать много оперативной памяти и увидите очень низкую производительность.
Вместо этого для очень больших файлов рассмотрите возможность использования MemoryMappedFile (см .: http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx ). Используя этот метод, вы можете работать с файлом практически неограниченного размера, не загружая все это в память.
При некотором значении String.length(), вероятно, около 5MB, использовать String уже не совсем практично. String оптимизирована для коротких фрагментов текста.
Подумайте о том, что произойдет, если вы сделаете
msString += " more chars"
Что-то вроде:
Система вычисляет длину myString плюс длину " more chars"
Система выделяет это количество памяти
Система копирует myString в новую область памяти
Система копирует " more chars" в новую область памяти после последнего скопированного символа myString
Оригинальная myString остается на милость сборщика мусора.
Хотя это хорошо и аккуратно для маленьких кусочков текста, это кошмар для больших строк, просто найти 2 ГБ непрерывной памяти, вероятно, будет проблемой.
Поэтому, если вы знаете, что будете обрабатывать более нескольких мегабайт символов, используйте один из классов *Buffer.
Верно, максимальная длина будет равна размеру Int32, однако вы, скорее всего, столкнетесь с другими проблемами памяти, если все равно имеете дело со строками большего размера.
Даже в x64-версиях Windows меня поразил .Net, ограничивающий каждый объект до 2 ГБ.
2 ГБ - это довольно мало для медицинского изображения. 2 ГБ - это даже мало для загружаемого образа Visual Studio.