вторник, 25 июня 2013 г.

Вопросы на знание строк в .NET

Меня всегда веселят .NET-разработчики, которые говорят, что они всё знают про строки. Не, ну есть и такие, которые действительно всё знают. Но обычно я с лёгкостью накидываю несколько вопросов, после которых мой собеседник теряет значительную долю уверенности в собственных знаниях. Предлагаю вам список из нескольких достаточно простых вопросов на знание типа System.String.

  • Различаются ли по скорости сравнение строк нижнего регистра и верхнего регистра? Будет ли различаться время выполнения команд "abc1" == "abc2" и "ABC1" == "ABC2"?
    В FCL сравнение строк в верхнем регистре оптимизировано, в общем случае лучше всегда использовать верхний регистр. Но в данном примере команды выполнятся одинаково быстро: оптимизация затрагивает только специфические локали.
  • Пусть у нас имеется массив различных строк. Может ли метод сортировки от раза к разу давать разные результаты?
    Да, стандартная сортировка в .NET является неустойчивой, а значит, если мы, скажем, сортируем строки без учёта регистра, то строки "AAA" и "aaa" могут расположиться в любом порядке.
  • Может ли String.CompareTo вернуть 0 для строк разной длины?
    Да, например строки "ß" (Эсцет) и "ss" совпадут в немецкой локали.
  • Могут ли две строки совпасть по методу String.CompareTo, но различаться по методу String.Equals?
    Да, т.к. по умолчанию CompareTo выполняется с учётом региональных стандартов, а Equals — без.
  • Является ли сравнение строк после приведения к верхнему или нижнему регистру эквивалентом сравнения строк без учёта регистра?
    Нет, в некоторых локалях такой фокус может не сработать. См. The Turkey Test.
  • Пусть у нас имеется две строки из маленьких латинских букв. Как их сравнить быстрее всего?
    С использованием StringComparison.Ordinal, т.к. в данном случае нет необходимости учитывать регистр и региональные настройки.
  • При форматировании даты (DateTime.ToString()) по умолчанию будет использоваться CurrentUICulture или CurrentCulture?
    CurrentCulture, т.к. данный пример не имеет отношения к графическому интерфейсу пользователя.
  • В чём разница между
    string s = "Line1\nLine2";
    
    и
    string s = "Line1" + Environment.NewLine + "Line2";
    
    Второй вариант является более универсальным, т.к. не зависит от платформы.
  • Как включить в литеральную строку знак обратного слэша (\) без использования управляющей последовательности?
    Нужно использовать verbatim string: @"\".
  • Может ли код
    Console.WriteLine("Hello");
    
    вывести строку, отличную от "Hello" при использование стандартного метода Console.WriteLine?
    Да, если строка интернирована, а кто-то через неуправляемую память добрался до хеш-таблицы интернированных строк и изменил целевое значение.
  • Если мы пометим сборку атрибутом System.Runtime.CompilerServices.CompilationRelaxationsAttribute с флагом CompilationRelaxations.NoStringInterning из того же пространства имён, то значит ли это, что литеральные строки в этой сборке не будут интернироваться?
    Нет. Спецификация ECMA гласит, что в этом случае CLR только может не интернировать все строки, но не обязана. Кроме того, мы всегда может заинтернировать строку через метод String.Intern
  • Если мы всё-таки отключили механизм интернировать строк под данную версию CLR, а в коде литеральная строчка "Hello" встречается дважды, то сколько раз она будет встречаться в метаданных сборки?
    Один. Интернирование строк происходит во время выполнения программы и не имеет отношения к метаданным.
  • Будет ли происходить копирование символьного массива при вызове метода StringBuilder.ToString()?
    В .NET 2.0: Нет, в новая строка будет ссылаться на тот же символьный массив, что и исходный StringBuilder.
    В .NET 4.0: Да, в этой версии платформы массив всегда копируется.
  • Допустим, у нас имеется StringBuilder, хранящий строчку из трёх символов. Возможен ли сценарий, в котором при замене первого символа произойдёт выделение памяти под новый символьный массив?
    Да, если предыдущим методом был StringBuilder.ToString(), а используемая версия .NET меньше 4.0.
  • Хранить в обычных строках секретные данные (например, пароль) нельзя, т.к. строка хранится в памяти в открытом виде, злоумышленники могут легко до неё добраться. Какими стандартными средствами можно защитить данные?
    Нужно хранить строчку с использованием класса SecureString.
  • Может ли количество текстовых элементов (которые можно получить через TextElementEnumerator) строки отличаться от количество образующих её char-символов?
    Да, т.к. FCL поддерживает кодировки, использующие больше 16-ти разрядов: один текстовый элемент может определяться несколькими char-символами.

Хорошие материалы для чтения:

5 комментариев:

  1. Для StringBuilder устаревшие данные. В 4.0 StringBuilder.ToString всегда выделяет новый массив, из-за того, что StringBuilder хранит данные в chunk’ах.

    ОтветитьУдалить
  2. Вот убивают такие "всезнайки"... : "а кто-то через неуправляемую память добрался до хеш-таблицы интернированных строк и изменил целевое значение."

    Вопрос в том же ключе:

    Можно ли задать значение поля пользовательского объекта значимого типа, помеченной атрибутом readonly, не используя конструктор?

    ОтветитьУдалить
  3. Вообще говоря, да: клонируешь структуру, убираешь атрибут readonly, и используя небезопасный доступ к объекту по указателю, в коде преобразуешь указатель на структуру с полем только-для-чтения в указатель на клонированную-структуру-с-полем-для-чтения-записи.

    ОтветитьУдалить
    Ответы
    1. Согласен с вами, хороший вопрос. И я считаю, что обсуждение таких вопросов весьма полезно. Во-первых, разбираясь в таких вещах, вы начинаете лучше понимать платформу, с которой работаете. Во-вторых, такие моменты могут пригодиться, если вы будете заниматься вопросами безопасности приложения. Но я ни в коем случае не агитирую использовать подобные вещи повсеместно в продакш-коде. Просто это вещи, о которых не помешает знать.

      Удалить