Строки и тупые оптимизаторы из MS
От: Kolesiki  
Дата: 22.06.21 21:24
Оценка: -10 :))) :))) :))) :)))
Тема для многих не новая, но её тупость не теряет актуальности. Речь идёт об "интернировании строк", коей MS конечно же горда до усрачки!
Грабли, которые она заботливо разложила, не нужно долго искать, вот простой код:

var s1 = "stroka";
var s2 = "stroka";

var lst = new List<string>();
lst.Add(s1);
lst.Add(s2);
Console.WriteLine("I=" + lst.IndexOf(s2));


Как вы уже догадались, выведется "I=0". НО ПОЧЕМУ?? Где логика? В какой идиотской стране два одинаково выглядящих дома имеют ОДИН АДРЕС?
А чем строки отличаются от домов? Да ничем! Выглядеть одинаково != быть одним и тем же.
Но здесь ручка грабель только начинается, вот слегка модифицированный код:

var s1 = "stroka";
var s2 = "stroka";

var lst = new List<string>();
lst.Add(s1);
lst.Add("чупакабра");
lst.Add(s2);
lst.Remove(s2);
Console.WriteLine(Fmt.JSON(lst));


Вывод (логичный с т.з. MS) будет ["чупакабра","stroka"] , но мы же хотели удалить ВТОРОЙ экземпляр "stroka", какого чёрта?? Мы-то (наивные) ожидаем ["stroka", "чупакабра"]!
Чувствуете, как на лбу почёсывается шишка? Вот это оно! ЗАБОТА и "преждевременная оптимизация" 20-летних усатых "сеньоров". Как так можно глупо подходить к проблеме?? Неужели им самим не прилетало от подобных оптимизаций? Ах, да... они же "пилят компилятор", какое им дело до ваших... хм... ничтожных "прикладных задач"!
Думаете, в Core кто-то уберёт эти грабли? Как бы не так! Будут жрать, плакать, снова колоться, но кактус не бросят.

По-уму, надо было оставить сравнение строк (s1 == s2) ровно таким же, как для объектов — через сравнение ссылок. И ТОЛЬКО если речь идёт о контенте, сделать отдельный оператор
(s1 eq s2) или case-insensitive вариант (s1 ieq s2). Всё. Но разве может "поколение с бейсиком головного мозга" думать категориями Си?! Не даром они этот бейсик до 2021 года дотащили — по уму и инструмент.

Кому-то задачка может показаться узконаправленной, но вот не так это. Всплыла она на "простой ГУЙне", где всего-то и нужны были упорядоченные строки. "Чего может быть проще?", — подумал мой мозг и забыл надеть каску.

Ну и напоследок вопрос: кому-то вообще пригождались их вот эти "интернирования"?? Что, прямо в каждой программе миллионы одинаковых строк, что надо было усираться и экономить байты? Сэкономили — с гулькин писюн, а насрали на 20 лет вперёд! И они за это ещё получают зарплату...
Re: Строки и тупые оптимизаторы из MS
От: Kolesiki  
Дата: 22.06.21 21:30
Оценка:
Да, и чтобы мой гнев был полезным, вот в какой код вылилась простая задача "удалить из листбокса выделенные строки"!

var gen = lbxExtraData.ItemContainerGenerator;
var victimIdxs = new List<int>();
for (int i = extData.Count - 1; i >= 0; i--) { // extData - это враппер (List<string>)lbxExtraData.ItemsSource
    var iv = (ListBoxItem)gen.ContainerFromIndex(i);
    if (iv.IsSelected) victimIdxs.Add(i);
}
foreach (var idx in victimIdxs)
    extData.RemoveAt(idx);
lbxExtraData.RefreshView();


Вместо того, чтобы просто взять выделенные элементы (в отдельный массив) и удалить их из списка. Да...
Re: EqualityComparer<T>.Default
От: Qbit86 Кипр
Дата: 22.06.21 22:09
Оценка: 38 (1) +3
Здравствуйте, Kolesiki, Вы писали:

K>Тема для многих не новая, но её тупость не теряет актуальности.


Тупость не теряет актуальности, но вот Майкрософта ли?..

K>Речь идёт об "интернировании строк"


Интернирование тут ни при чём. При чём дефолтный компаратор. Если ты не передаёшь компаратор в явном виде, то подписываешься на то, что его выберут за тебя.

K>В какой идиотской стране два одинаково выглядящих дома имеют ОДИН АДРЕС?

K>А чем строки отличаются от домов? Да ничем! Выглядеть одинаково != быть одним и тем же.

Вот тебе два разных одинаково выглядящих дома, имеющих разный адрес:
string s1 = "STRoka".ToLowerInvariant();
string s2 = "strOKA".ToLowerInvariant();
bool isInterned = string.IsInterned(s1) is not null;
Console.WriteLine($"{nameof(isInterned)}: {isInterned}"); // False
bool areReferenceEqual = ReferenceEquals(s1, s2);
Console.WriteLine($"{nameof(areReferenceEqual)}: {areReferenceEqual}"); // False
bool areSemanticallyEqual = string.Equals(s1, s2, StringComparison.Ordinal);
Console.WriteLine($"{nameof(areSemanticallyEqual)}: {areSemanticallyEqual}"); // True
List<string> list = new() { s1, s2 };
Console.WriteLine($"{nameof(list.IndexOf)}({nameof(s2)}): {list.IndexOf(s2)}"); // 0


K>Неужели им самим не прилетало от подобных оптимизаций?




K>По-уму, надо было оставить сравнение строк (s1 == s2) ровно таким же, как для объектов — через сравнение ссылок.


Обобщённые алгоритмы стандартной библиотеки не используют operator ==. Они используют либо переданный компаратор, либо дефолтный, который в свою очередь резолвит сравнение через переопределённый метод Equals, или фоллбэчатся к сравнению ссылок.
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: EqualityComparer<T>.Default
От: Kolesiki  
Дата: 22.06.21 23:00
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Интернирование тут ни при чём. При чём дефолтный компаратор. Если ты не передаёшь компаратор в явном виде, то подписываешься на то, что его выберут за тебя.


Ну хорошо, Стамбул — город контрастов! Но ведь граблей-то это не отменяет! Вот ещё более жизненный пример:

var s1 = "stroka";
var s2 = Console.ReadLine();// здесь вводим stroka

var lst = new List<string>();
lst.Add(s1);
lst.Add("чупакабра");
lst.Add(s2);
lst.Remove(s2);
Console.WriteLine(Fmt.JSON(lst));


На выходе:

stroka
["чупакабра","stroka"]

Я согласен, с интернированием протупил, но компаратор-то всё равно с граблями! Нельзя просто за юзера решить "ты хочешь сравнить строки" (вместо адресов). Это не говоря о том, что даже само сравнение может быть ещё и case insensitive.
Re[3]: EqualityComparer<T>.Default
От: Qbit86 Кипр
Дата: 23.06.21 00:01
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>Нельзя просто за юзера решить "ты хочешь сравнить строки" (вместо адресов).


Поэтому большинство стандартных алгоритмов поиска/сравнения имеют перегрузки, принимающие на вход IEqualityComparer<T>. И только если пользователь игнорирует эти перегрузки, то алгоритмы используют EqualityComparer<T>.Default.

K>Это не говоря о том, что даже само сравнение может быть ещё и case insensitive.


Да, поэтому многие code style guide'ы и статические анализаторы предписывают явно задавать стратегию сравнения строк.

BCL предоставляет готовую пачку экземпляров компараторов строк StringComparer (реализующего IEqualityComparer<string?>) под разные нужды:
• StringComparer.Ordinal,
• StringComparer.InvariantCultureIgnoreCase, etc.
Их и нужно передавать в алгоритмы типа Dictionary<string, TValue>, Linq, etc.
И соответственно собственный библиотечный код пишут терминах IEqualityComparer<T>.

Прикладной же код может использовать для сравнения статические функции с третьим параметром — стратегией StringComparison:
• string.Equals(s1, s2, StringComparison.Ordinal),
• string.Equals(s1, s2, StringComparison.InvariantCultureIgnoreCase), etc.

Чего не нужно использовать для сравнения строк ни библиотечному, ни прикладному коду — operator ==() или экземплярный метод string.Equals().

K>Нельзя просто за юзера решить "ты хочешь сравнить строки" (вместо адресов).


Тебе нужно явно передать ReferenceEqualityComparer.Instance:

An IEqualityComparer{Object} that uses reference equality (object.ReferenceEquals(object?, object?)) instead of value equality (object.Equals(object?)) when comparing two object instances.
/ReferenceEqualityComparer.cs

А для поиска использовать List<T>.FindIndex(). Правда, он получает на вход Predicate<string>, а не IEqualityCmparer<T>, но это тривиально оборачивается лямбдой.

Да, здесь уже нужно учитывать, что литералы интернируются.
Но закладываться в логике на сравнение по ссылке вместо сравнения по значению — это в принципе какой-то супер-специфичный сценарий. «Так не носят.» В исходниках BCL этот ReferenceEqualityComparer почти не используется.
Глаза у меня добрые, но рубашка — смирительная!
Re: Строки и тупые оптимизаторы из MS
От: varenikAA  
Дата: 23.06.21 01:43
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>Тема для многих не новая, но её тупость не теряет актуальности.

K>
K>var s1 = "stroka";
K>var s2 = "stroka";
K>

Всё верно. Если s1 и s2 содержат одни и те же данные.
Критикуете — предлагайте.
Например, пусть ИИ догадывается, что вам нужно удалить.
Как правильно заметили есть RemoveAt, которым я неоднократно пользовался ранее.
Спасибо, что напомнили!
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re: Строки и тупые оптимизаторы из MS
От: Mr.Delphist  
Дата: 23.06.21 08:43
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>но мы же хотели удалить ВТОРОЙ экземпляр "stroka", какого чёрта??


Второй? А откуда это должно следовать? Не, я понимаю, что с сишным бэкграундом всё должно казаться гвоздями указателями, но надо над собой работать
Re: Строки и тупые оптимизаторы из MS
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.06.21 08:52
Оценка: +4
Здравствуйте, Kolesiki, Вы писали:

K>Тема для многих не новая, но её тупость не теряет актуальности. Речь идёт об "интернировании строк", коей MS конечно же горда до усрачки!

Интернирование не при чем.
Строки обладают структурной эквивалентностью, то есть ведут себя как value-типы.
Это все в спецификации написано, стоило бы почитать.
Re[2]: Строки и тупые оптимизаторы из MS
От: Silver_S Ниоткуда  
Дата: 24.06.21 11:38
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>Да, и чтобы мой гнев был полезным, вот в какой код вылилась простая задача "удалить из листбокса выделенные строки"!


Даже если бы для List<string>.Remove изменили поведение. Тогда бы гнев переключился бы на List<int>.Remove, List<char>.Remove ? В них все равно придется конкретнее указывать — что нужно удалить.
Если приходится извращаться, то это недостатки ListBox, а не List<T>.Remove.
Отредактировано 24.06.2021 11:40 Silver_S . Предыдущая версия .
Re[2]: Строки и тупые оптимизаторы из MS
От: karbofos42 Россия  
Дата: 24.06.21 14:37
Оценка: :))
Здравствуйте, Kolesiki, Вы писали:

K>Да, и чтобы мой гнев был полезным, вот в какой код вылилась простая задача "удалить из листбокса выделенные строки"!

...
K>Вместо того, чтобы просто взять выделенные элементы (в отдельный массив) и удалить их из списка. Да...

Жду сообщений от пользователей, которые будут спрашивать как им в ListBox'е из двух одинаковых строк выбрать ту, которую они действительно хотят удалить, а не ту, которая точно так же выглядит.
Re: Строки и тупые оптимизаторы из MS
От: Воронков Василий Россия  
Дата: 25.06.21 08:44
Оценка:
Здравствуйте, Kolesiki, Вы писали:

А причем тут интернирование? Есть ссылочная эквивалентность, есть структурная. Вот пример с аналогичным поведением без всяких строк:

record Address
{
    public string Street { get; init; }
    public int House { get; init; }
}

class Program
{
    static void Main(string[] args)
    {
        var a1 = new Address { Street = "Svobody", House = 2 };
        var a2 = new Address { Street = "Svobody", House = 2 };
        var xs = new List<Address> { a1, a2 };
        Console.WriteLine(xs.IndexOf(a2) == 0);
        Console.WriteLine(ReferenceEquals(a1, a2));
    }
}


Выводит:

True
False


Интернирование — это механизм оптимизации, который вообще никак на поведение не влияет, т.к. строки в дотнете неизменяемые. Да, есть unsafe, где можно натворить дел, но вроде как само слово "unsafe" недвусмысленно намекает, что, прежде чем туда лезть, неплохо бы в остальном хорошо разобраться.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.