В 2016 году на работе я начал постить в чатик разные задачки-загадки по C#. Например, про использование KeyValuePair в качестве ключей словаря.
Делалось это для создания полезной в профессиональном плане движухи среди разработчиков (оказалось не очень эффективно).
Лирическое отступление:
В университете со второго по четвёртый семестр преподавали Perl. И курс этот вызывал много недоумения. На контрольных мероприятиях нам задавали нетривиальные вопросы – однажды мой одногруппник сказал «да там нужные такие эзотерические знания! Откуда они у первокурсников?». На четвёртом году обучения мне довелось слушать выступление нашего лектора на конференции по Perl. Из его доклада я вынес, что Perl в таком ключе нам преподавали с мыслью о том, что это должно пробудить в нас computer scientist'ов, которые будут задавать вопросы и искать на них ответы. Например, почему для переменных-массивов (@) и переменных-хэш-таблиц (#) разный синтаксис, хотя и там, и там очень похожий синтаксис доступа []? Этот вопрос должен был привести нас к понимаю того, что массив и хэш-таблица очень по-разному устроены внутри, и у каждого есть особенности использования.
Вот и задачки подспудно я постил с примерно таким намерением – начнёшь думать над решением, задаваться вопросами – узнаешь что-то полезное о языке или фреймворке.
А ещё хорошую мотивашку можно прочитать у Акиньшина:
Рекомендуется относиться к заданиям не как к способу проверить ваши знания, а как к начальной точке для обсуждения тех или иных аспектов платформы. Если вы обнаружили что-то новое для себя, то это отличный повод поизучать .NET чуть подробней. Попробуйте поиграться с кодом: модифицируйте его и изучайте, как изменения влияют на результат. Почитайте соответствующую литературу. Подумайте, как новые знания могут пригодиться вам в вашей работе.
Было ещё соображение хотя бы минимальной практичности, чтобы это возможно было хоть как-то применить или встретить в реальной трудовой деятельности. Например, задачка про KeyValuePair из начала статьи мне кажется практичной (на самом деле другие мои коллеги принести это мне как баг с прода). А задачка вида
dynamic foo = null;
dynamic bar = null;
var buffer = foo + 1 == bar; // ?
таковой уже не кажется (её в чатик запостил один из тех немногих коллег, которые поддержали движуху).
Кстати, эта задачка легко проверяется в C# interactive или https://try.dot.net
Некоторые задачки я считал антимаркерами. Если человек знает её и ответ на неё, то, возможно, он слишком гик, перечитал Скита, и фанатеет не за то. Например, задачка о null, не кидающем NRE
Мысленно я делил задачки на две группы: нормальные и из серии «Десять невероятных фактов о .net» вида «Яблоко от яблони упало за 10 километров!!1!».
Сейчас я думаю, что любая задачка интересна в плане пробудить исследователя и выяснить почему так. Может найтись инженерная история. Или менеджерская (выбор в пользу другого решения привёл бы к тому, что у тысячи пользователей ворнинги компиляции стали бы ошибками. Никому не хочется после выпуска новой версии получить тысячи обращений вида «Мы обновились, у нас всё сломалось»).
Но опять-таки вопрос прагматичности. Я вот не работаю с dynamic (к стыду своему, я не понимаю, зачем эту фичу добавили, хотя осознаю, что не просто так). Поэтому вопросы про dynamic мне кажутся баловством.
И работа со строками на том уровне, чтобы мне стали важны вопросы их интернирования, представляется мне чем-то далёким.
Далее списком «Десять невероятных фактов о .net»:
- Эту я подавал с «Добавление кода в конец программы изменяет поведение кода перед ним. Удивительно! Метод, начинающийся на Is, но возвращающий НЕ bool. Удивительно!»
static void Main(string[] args)
{
string s = new string(new char[] { 'x', 'y', 'z' });
Console.WriteLine(string.IsInterned(s) ?? "not interned");
string.Intern(s);
Console.WriteLine(string.IsInterned(s) ?? "not interned");
Console.WriteLine(ReferenceEquals(string.IsInterned(new string(new char[] { 'x', 'y', 'z' })), s));
}
static void Main(string[] args)
{
string s = new string(new char[] { 'x', 'y', 'z' });
Console.WriteLine(string.IsInterned(s) ?? "not interned");
string.Intern(s);
Console.WriteLine(string.IsInterned(s) ?? "not interned");
Console.WriteLine(ReferenceEquals(string.IsInterned(new string(new char[] { 'x', 'y', 'z' })), s));
Console.WriteLine(ReferenceEquals("xyz", s)); // the difference is here!
}
Ответ: Причина изменения поведения всей программы в том, что код содержит литерал «xyz». А когда вы добавляете литерал в вашу программу, CLR автоматически добавляет его в пул интернирования ещё до начала выполнения программы.
Подробности в статье String.Intern делает строки ещё интереснее
- Что может быть напечатано в консоли в результате выполнения кода
partial class C
{
static void Main()
{
int i = 3;
C.M(i = 5);
Console.WriteLine(i);
}
}
Ответ: Из этого кода нельзя дать ответ. Можно объявить partial-метод M, но не определять его реализацию.
partial class C
{
static void Main()
{
int i = 3;
C.M(i = 5);
Console.WriteLine(i);
}
}
partial class C
{
static partial void M(int i);
}
Тогда компилятор не сгенерирует вызов метода, и, соответственно, вычисление аргумента будет опущено. И в результате на консоль будет выведено 3.
Но если реализовать метод M, то будет напечатано 5.
Подробности в https://docs.microsoft.com/ru-ru/archive/blogs/wesdyer/in-case-you-havent-heard