#c_sharp #unsafe
Думаю всем известно что строки (System.String) неизменяемые по своей природе. Поэтому
часто работа с такими методами, как string.ToUpper() и прочими, порождает новый объект,
почти идентичный предыдущему.
Я даже не хочу выяснять причины этого, у меня явно ума поменьше чем у ребят, которые
придумали такую обертку над массивом Char, но все же изменить строки можно, как минимум
через указатели.
Написал такой расширяющий метод:
public static unsafe String ToUpperUnsafe ( this String value )
{
fixed ( Char* arrChr = value )
{
for ( var i = 0 ; i < value.Length ; ++i )
{
var temp = Char.ToUpper( value[ i ] );
arrChr[ i ] = temp;
}
}
return value;
}
Как видно, на вход принимается один объект, изменяется и возвращается без клонирования.
Вопрос в том, насколько это безопасно. Смотрел бегло исходники оригинального метода,
там много всего, что я не понял, из-за этого возникла мысль, что в определенных ситуациях
это может не прокатить.
p.s. Кстати ванильный метод работает быстрей, зато этот выигрывает в памяти.
Ответы
Ответ 1
Так делать нельзя. Дело в том, что весь framework исходит из того, что строки неизменяемы. Поэтому, например, если вы поместите строку в HashSet, а потом измените её, вы потом не сможете её оттуда удалить (а вместе с вами и другие куски программы, находящиеся далеко от вас и не знающие, что вы сломали строку). Метод сортировки, который не учитывает, что у него из-под носа могут заменить значение, имеет право зациклиться бесконечно или вылететь по обращению к неправильному индексу. Представьте себе, что будет, если строка, которую вы ломаете, интернирована. Тогда каждый, у кого была строковая константа "abc", внезапно без предупреждения получит строковую константу "ABC": string lo = "abc", hi = "ABC"; string villain = "abc"; villain.ToUpperUnsafe(); Console.WriteLine(lo == hi); // true Представьте себе так же, что вы сломаете строку, которая является названием типа. Что произойдёт при попытке применить рефлексию? Словом, ваш метод привносит в язык undefined behavour, отсутствие которого выгодно отличало C# от C++. Вся эта катавасия, которую может устроить одна «маленькая» оптимизация, совершенно не стоит выгоды в несколько микросекунд.Ответ 2
Вопрос в том, насколько это безопасно. Да вообще небезопасно. Думаю всем известно что строки (System.String) неизменяемые по своей природе. ... Я даже не хочу выяснять причины этого Зря не хочешь. Представь всё, что записывается литералами. Это числа. В VB ещё даты. Всё это value-типы. int x = 50; x.Add(10); // Ну представим, что такой метод есть int y = 50; // Ну ты же не ждёшь, что теперь y равен 60? Аналогично со строками: string s = "abc"; s.ToUpper(); string t = "abc"; // Упс.. Твоя реализация сделает t = "ABC" По-моему, одного этого достаточно, чтобы никогда твой метод не использовать. А теперь, почему же reference-тип? Да это просто оптимизация - не надо копировать огромные строки при каждой передаче. Мы просто не меняем оригинал, поэтому семантически нет разницы между value и reference. А вот если мы хотим поменять, то надо создать новую строку.Ответ 3
Вы только что изобрели велосипед :), используя unsafe, вместо класса StringBuilder, созданный именно для представления изменяемых строк.
Комментариев нет:
Отправить комментарий