Skip to content

Latest commit

 

History

History
101 lines (78 loc) · 8.88 KB

2021-07-29 как я задачки по C# загадывал.md

File metadata and controls

101 lines (78 loc) · 8.88 KB

В 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»:

  1. Эту я подавал с «Добавление кода в конец программы изменяет поведение кода перед ним. Удивительно! Метод, начинающийся на 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 делает строки ещё интереснее

  1. Что может быть напечатано в консоли в результате выполнения кода
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