Комментарии 37
При перечислении объектов для вызова следует использовать оператор yield return, а не создавать возвращающую коллекцию.
"В зависимости от ситуации" явно пропущено.
Пункт 2 — сомнителен. Активное использование ленивых вычислений (а yield return
— это ленивые вычисления) может привести к тому, что исключение произойдет совсем не там где оно ожидалось.
yield return
сам по себе — лучше чем создание коллекции только чтобы ее вернуть — но бросаться переписывать уже готовую программу не следует. Могут быть сюрпризы.
По пункту 4 — забыли рассказать что делать если исключение нужно сохранить, а потом бросать заново уже в другом месте. А нужно сохранять не исключение, а ExceptionDispatchInfo
. На новых фреймворках.
В старых фреймворках можно вызвать у исключения метод InternalPreserveStackTrace
через рефлексию. То есть, конечно же, не напрямую, а создав делегат.
По пункту 8. Так в чем ошибка или ловушка-то заключаются? Это же очевидное ожидаемое поведение...
он рендерит бесполезный классНа экран, что ли, рендерит? :) Правильно: делает класс бесполезным. Учите английский.
Т.е. компилятор выбирает более специфический строковый метод при втором вызове.
И что здесь не так?
Универсальный метод — для вообще всех данных.
А не универсальный — для обработки с конкретными типами.
Ловушка в чем? В том, что мы сделали метод для строки и он вызывается… для строк?
А какого поведения здесь ждет программист?
Когда знаешь как работают generic, понимаешь, что компилятор еще один метод с той же сигнатурой не создаст.
А когда для тебя это магическая функция, принимающая что угодно...
Что у тебя две функции и обе принимают string.
Вот тут и ловушка. Это ж магическое программирование. В зависимости от ситуации.
Вы мне объясняете, как дженерики работают? Я знаю. Я объясняю, где тут может быть проблема в восприятии
Проблема в восприятии может быть с чем угодно, стоит только зафиксировать уровень читателя на определенном уровне. Для программиста, который хотя бы универ закончил (не говоря уже про год+ практической работы) это совсем не магия. А таких — большая часть и читателей хабра, и разработчиков в принципе.
в том смысле что объявленные в классе поля все равно успевают инициализироваться, несмотря на то что конструктор не отрабатывает
Почему нельзя построить сравнивающий Expression Tree при первом вызове сравнения структур данного типа и закешировать его?
А зачем? Если у вас в value type есть ссылочные поля, логика Equals
, скорее всего, прикладная, а в этом случае вам все равно надо писать свою реализацию.
А сравнение value types, состоящих из value types — быстрое.
ValueType.Equals будет использовать отражение
Рефлексия.
https://msdn.microsoft.com/ru-ru/library/bsc2ak47(v=vs.110).aspx
В англоязычной: reflection
В русскоязычной: отражение
Или русскоязычная документация ошибочна?
10 пункт странный. Если структуры нужно сравнивать, то в любом случае нужно реализовывать IEquitable<T>
, а рассуждать, что метод в 10 раз медленнее лучше, чем в 100 раз медленнее особого смысла нет.
А во-вторых, вот нужно мне стрингу засунуть в структуру, которую я хочу сравнивать — автор по факту говорит мне, что структуру в таком случае использовать нельзя :) Хотя все решается тривиально. По сути фреймворк просто зря имеет Equals и GetHashCode в object'е. С другой стороны, я понимаю, зачем это сделано — тут начинаются возня с мониторами, локами, сохранением состояния блокировки в хэше объекта и прочие прелести… То есть есть причины, почему все это в object'е хранится. Но по сути все кастомные структуры со сравнением обязаны реализовывать IEquitable<T>
. Можно хоть roslyn-анализатор ставить, чтобы Equals вызывался исключительно интерфейсный, а не object'овый.
Лучше roslyn-генератор, чтобы дописывал нормальную реализацию :)
1, 5, 6, 8 и 9 справедливы и для Java. Всё же несильно языки различаются =)
Частичной специализации шаблона в C# нет. Номер 8 — это простая перегрузка метода.
Неоднозначность может проявляться вот в таких случаях:
public static void ExecuteTest<TValue>(TValue value)
{
GenericTest.Test(value);
}
public static void Foo()
{
ExecuteTest(123); // Generic method
ExecuteTest("123"); // Generic method - все же поняли, что это не С++?
}
С#: 10 распространенных ловушек и ошибок