Каверзные вопросы по C#


    Хочу представить вашему вниманию комические купле каверзные вопросы по C#.
    Не удержался и решил запостить немного классики.
    Некоторые вопросы в подборке кому-то могут показаться слишком простыми, но небольшой подвох в них, как правило, есть. Иногда можно и простым вопросом подловить. Будут полезны тем, кто изучает язык.
    Всех, кому интересно, прошу под кат!

    1 Какой будет результат выполнения следующего кода?

       static String str;
       static DateTime time;
    
            static void Main(string[] args)
            {
                Console.WriteLine(str == null ? "str == null" : str);
                Console.WriteLine(time == null ? "time == null" : time.ToString());
                Console.ReadLine();
            }
    

    Ответ
    str == null
    1/1/0001 12:00:00 AM

    Обе переменные не инициализированы, но string это ссылочный/reference тип (если быть более точным, то это immutable тип, что означает reference тип с семантикой value типа), а DateTime это тип значения/value type. Значение по умолчанию неинициализированного типа DateTime это 12:00 1 января 1 года.


    2 Поиграем с наследованием. Что будет выведено на экран?

            class A
            {
                public void abc(int q)
                {
                    Console.WriteLine("abc из A");
                }
            }
    
            class B : A
            {
                public void abc(double p)
                {
                    Console.WriteLine("abc из B");
                }
            }
    
            static void Main(string[] args)
            {
                int i = 5;
                B b = new B();
                b.abc(i);
                Console.ReadLine();
            }
    

    Ответ
    abc из B


    3 Похожий вопрос. Что будет результатом?

            class P
            { }
            class Q : P
            { }
    
            class A
            {
                public void abc(Q q)
                {
                    Console.WriteLine("abc из A");
                }
            }
    
            class B : A
            {
                public void abc(P p)
                {
                    Console.WriteLine("abc из B");
                }
            }
    
            static void Main(string[] args)
            {
                B b = new B();
                b.abc(new Q());
                Console.ReadLine();
            } 
    

    Ответ
    abc из B
    Здесь все чуть более очевидно по сравнению с прошлым примером.


    4 Типичный «развод» на понимание полиморфизма. Главное ничего не забудьте и не упустите из виду.
    Какой будет результат выполнения следующего кода?

        class Program
        {
            static void Main(string[] args)
            {
                MyClassB b = new MyClassB();
                MyClassA a = b;
                a.abc();
                Console.ReadLine();
            }
        }
    
        class MyClassA
        {
            public MyClassA()
            {
                Console.WriteLine("constructor A");
            }
    
            public void abc()
            {
                Console.WriteLine("A");
            }
        }
    
        class MyClassB:MyClassA
        {
            public MyClassB()
            {
                Console.WriteLine("constructor B");
            }
    
            public void abc()
            {
                Console.WriteLine("B");
            }
        }
    

    Ответ
    constructor A
    constructor B
    A

    При инициализации класса B будет выполнен конструктор по умолчанию класса А, потом конструктор класса B. После присвоения переменной типа класса А значения b мы получим в ней экземпляр класса B. Казалось бы должна быть вызвана abc() из класса B, но так как в классе B не указан никакой предикат у метода abc, то выходит что он скрывает abc из класса A. Пример не совсем правильный и abc() в классе B будет подчеркнуто, так как требуется предикат new.


    5 У меня есть такой вот класс:

            public class Point
            {
                public int X { get; set; }
                public int Y { get; set; }
                public Point(int xPos, int yPos)
                {
                    X = xPos;
                    Y = yPos;
                }
            }
    

    И есть 3 экземпляра класса. Сработает ли подобная инициализация третьего экземпляра? Если нет, то что нужно сделать?

                Point ptOne = new Point(15, 20);
                Point ptTwo = new Point(40, 50);
                Point ptThree = ptOne + ptTwo;
    

    Ответ
    Пример работать, конечно же, не будет. Для того, чтобы этот код заработал, в класс Point нужно добавить перегрузку оператора сложения. Например, вот такой:

                public static Point operator +(Point p1, Point p2)
                {
                    return new Point(p1.X + p2.X, p1.Y + p2.Y);
                }
    



    6 Какой будет результат выполнения следующего кода?

            string result;
    
            private async void btnStart_Click(object sender, RoutedEventArgs e)
            {
                SaySomething();
                txtSomeTextBlock.Text = result;
            }
    
            async System.Threading.Tasks.Task<string> SaySomething()
            {
                await System.Threading.Tasks.Task.Delay(1000);
    
                result = "Hello world!";
                return result;
            }
    

    Ответ
    Будет выведена пустая строка, а не «Hello world!». Таск SaySomething() был вызван без await и потому SaySomething выполняется синхронно до первого await, то есть до строки

    await System.Threading.Tasks.Task.Delay(1000);
    

    После чего выполнение возвращается к btnStartClick. Если использовать await при вызове SaySomething(), то результат будет ожидаемым и на экран будет выведен текст «Hello world!»


    7 Вопрос из разряда «must know». Какой будет результат выполнения следующего кода?

        delegate void SomeMethod();
    
            static void Main(string[] args)
            {
                List<SomeMethod> delList = new List<SomeMethod>();
                for (int i = 0; i < 10; i++)
                {
                    delList.Add(delegate { Console.WriteLine(i); });
                }
    
                foreach (var del in delList)
                {
                    del();
                }
            }
    

    Ответ
    Программа отобразит число 10 десять раз.
    Делегат был добавлен 10 раз. Причем была взята ссылка на переменную i. Ссылка, а не значение. Вот поэтому при вызове делегата берется последнее значение переменной i. Это типичный пример замыкания (closure)


    8 Что будет выведено на экран следующим кодом?

            static bool SomeMethod1()
            {
                Console.WriteLine("Метод 1");
                return false;
            }
    
           static bool SomeMethod2()
            {
                Console.WriteLine("Метод 2");
                return true;
            }
            static void Main(string[] args)
            {
                if (SomeMethod1() & SomeMethod2())
                {
                    Console.WriteLine("блок if выполнен");
                }
            }
    

    Ответ
    Метод 1
    Метод 2
    Блок if выполнен не будет, так как SomeMethod1 возвращает false. Но, так как используется логический оператор &, то будет проверено и второе условие – SomeMethod2. Если бы использовался более привычный оператор &&, то проверено было бы только значение первого метода.


    9 Еще один простой (даже можно сказать бородатый) вопрос. Что будет результатом выполнения следующего кода?

                double e = 2.718281828459045;
                object o = e; // box
                int ee = (int)o;
    

    Ответ
    Этот код работать не будет и вызовет исключение в последней строке. Хотя казалось бы следующий кастинг (casting или иначе выражаясь explicit conversion) ошибки не вызывает, а только теряет дробную часть числа.

                double e = 2.718281828459045;
                int ee = (int)e;
    

    Но при unboxing-е происходит проверка, содержит ли объект значение запрашиваемого типа. И только после этой проверки значение копируется в переменную.
    А вот следующий, сделает unboxing без ошибки

                int ee = (int)(double)o; 
    

    Следующий код также сперва выполнить приведение объекта o к типу dynamic и затем без проблем произведет уже casting, а не unboxing:

                int ee = (int)(o as dynamic);
    

    Впрочем, он эквивалентен следующему коду:

         int ee = (int)(o is dynamic ? (dynamic)o : (dynamic)null);
    

    и в результате фактически будет идентичен первому примеру:

                int ee = (int)(dynamic)o;
    

    хотя, казалось бы, является новым трюком.


    10 Что произойдет в результате выполнения данного кода?

                float q = float.MaxValue;
                float w = float.MaxValue;
    
                checked
                {
                    float a = q * w;
                    Console.WriteLine(a.ToString());
                }
    

    Ответ
    На экран будет выведено:
    Infinity
    float и double не являются integral types и потому в случае использования checked переполнения не возникает. Хотя если бы мы использовали int, byte, short или long то ожидаемо возникла бы ошибка. Unchecked также не будет работать с не встроенными типами. Например:

                decimal x = decimal.MaxValue;
                decimal y = decimal.MaxValue;
    
                unchecked
                {
                    decimal z = x * y;
                    Console.WriteLine(z.ToString());
                }
    

    Сгенерирует исключение System.OverflowException


    11 Можно еще поиграть с типом decimal. Если что-то будет выведено на экран, то что?

                int x = 5;
                decimal y = x / 12;
                Console.WriteLine(y.ToString());
    

    Ответ
    Этот пример выведет 0 из-за того что x поленились привести к типу decimal. Так как x у нас целочисленного типа, то при делении 5 на 12 получается число меньше чем 1, значит это целочисленный ноль. Верный результат выведет строка:

       decimal y = (decimal)x / 12;
    



    12 Что произойдет в результате выполнения следующего кода:

                double d=5.15;
                d = d / 0;
    
                float f = 5.15f;
                f = f / 0;
    
                decimal dc = 5.12m;
                dc = dc / 0;
    

    Ответ
    Деление на 0 типов double и float вернет Infinity, а вот decimal вызовет исключение System.DivideByZeroException

    На всякий случай коротенько о различиях между decimal, double и float:
    decimal (128-битный тип данных, точностью 28–29 знаков после запятой) — используется в финансовых калькуляциях которые требуют высокой точности и отсутствия ошибок при округлении
    double (64-битный тип данных, точностью 15–16 знаков после запятой) — обычный тип для хранения значений с плавающей запятой. Используется в большинстве случаев (кроме финансовых)
    float (32-битный тип данных, точностью 7 знаков после запятой) — тип с самой низкой точностью и самым низким диапазоном, зато с самой высокой производительностью. Возможны ошибки при округлениях. Используется для высоконагруженных расчетов.


    13 Допустим, имеется такой вот метод:

    int SomeMethod(int x, int y)
        {
            return (x - y) * (x + y);
        }
    

    Можно ли вызвать его вот так:

    SomeMethod(y:17, x:21)
    

    Ответ
    Да, можно. Еще можно вызвать и вот так:

    SomeMethod(11, y:27)
    

    Но нельзя так:

    SomeMethod(x:12, 11)
    

    UPDATE: Начиная с C# 7.2 совершение такого вызова скорее всего станет возможным


    14 Что произойдет в результате выполнения данного кода?

            static void Main(string[] args)
            {
                int someInt;
                SomeMethod2(out someInt);
                Console.WriteLine(someInt);
    
                SomeMethod1(ref someInt);
                Console.WriteLine(someInt);
    
                SomeMethod(someInt);
                Console.WriteLine(someInt);
    
                Console.ReadLine();
            }
    
    
            static void SomeMethod(int value)
            {
                value = 0;
            }
            static void SomeMethod1(ref int value)
            {
                value = 1;
            }
            static void SomeMethod2(out int value)
            {
                value = 2;
            }
    

    Ответ
    Ничего страшного не произойдет. Будет выведено на экран:
    2
    1
    1
    Так как первым мы вызываем SomeMethod2 с ключевым словом out, то значит someInt может быть передана без инициализации. Если бы мы использовали SomeMethod или SomeMethod1, то возникла бы ошибка компиляции.
    Так как SomeMethod в параметре не содержит ключевого слова ref или out, то значение в этот метод передается по значению, а не по ссылке, а значит someInt не изменяется.
    Ключевые слова ref и out означают, что значения передаются по ссылке. Но во втором случае в методе параметру обязано быть задано значение. В нашем примере, в методе SomeMethod2 параметру value обязательно должно быть присвоено значение.


    15 Сработает ли код?

            static void Main(string[] args)
            {
                goto lalala;
                int i = 5;
                { Console.WriteLine(i); }
                lalala:
                Console.WriteLine("Прощай, жестокий мир! (=");
                Console.ReadLine();
            }
    

    Ответ
    Да, хоть и выглядит непривычно. На экран будет выведено только Прощай, жестокий мир!
    Внутри метода вполне можно объявить между фигурными скобками включающую локальную область. Переменные из этой области будут недоступны вне ее. То есть вот такой вот код не скомпилируется:

            static void Main(string[] args)
            {
                { int i = 10; }
                Console.WriteLine(i);
            }
    

    Как ни странно, но goto в C# все еще поддерживается. Хотя не особо нужен.


    16 Что будет выведено на экран

            string hello = "hello";
            string helloWorld = "hello world";
            string helloWorld2 = "hello world";
            string helloWorld3 = hello + " world";
    
            Console.WriteLine(helloWorld == helloWorld2);
            Console.WriteLine(object.ReferenceEquals(helloWorld, helloWorld2));
            Console.WriteLine(object.ReferenceEquals(helloWorld, helloWorld3));
            Console.ReadLine();


    Ответ
    Будет выведено: True, True, False
    Это типичный пример интернирования строк. Ситуации, когда строки, хранящие одно и то же значение представляют из себя один объект в памяти. Этот механизм позволяет чуть более экономно расходовать память.


    17 Можно ли в C# использовать указатели как в C++?

    Ответ
    Можно внутри метода объявленного с модификатором unsafe или внутри блока unsafe

    unsafe {  
        int a = 15; 
        *b = &a;
        Console.WriteLine(*b); 
              } 

    Необходимо не забыть в свойствах проекта указать «Allow unsafe code»


    18 Что такое дословные/точные строки (verbatim strings)?

    Ответ
    Все прекрасно знают что это за строки и постоянно используют их, но далеко не все знают название.
    Verbatim strings — это строки, которые начинаются с символа @ и в которых не обрабатываются escape-последовательности.
    Как их правильно называть на русском — дословные или точные это уже отдельный вопрос.


    Бонус:
    В чем отличие readonly и const?
    Может ли класс иметь static конструктор?
    Может ли быть try без catch?
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 86

      +6
      Кому нужно больше задачек, есть Задачник.NET.
      +12
      Помню, что, когда меня интервьюировали после университета, то тоже спрашивали такие вопросы. На самом деле вопрос был про виртуальные методы и оператор new, которого тут нет. У меня был простой ответный вопрос «А что, вы так пишите?».
        0
        для вопросов про сортировку такой ответ ещё хоть как-то канает. в данном случае не канает совсем, ибо во первых чужой код МОЖЕТ быть написан именно так. а во-вторых бывают случаи, когда имеет смысл написать именно так.
          +5
          а во-вторых бывают случаи, когда имеет смысл написать именно так.


          Это какие?
        +10
        Как ни странно, но goto в C# все еще поддерживается. Хотя не особо нужен.

        Вообще-то нужен. Конструкция такого вида недопустима в C# (согласно требованиям C# конец разделов switch должен быть недостижим)

        switch(variable)
        {
            case 1:
                Console.WriteLine("case 1");
                //need break, goto case, return or throw.
            case 2:
                Console.WriteLine("case 2");
                break;
            default:
                Console.WriteLine("default");
                break;
        }
        

        В MSDN предлагают использовать оператор goto в таких случаях. В итоге выходит такой код:

        switch(variable)
        {
            case 1:
                Console.WriteLine("case 1");
                goto case 2;
            case 2:
                Console.WriteLine("case 2");
                break;
            default:
                Console.WriteLine("default");
                break;
        }
        
          +3
          Такого рода конструкции не зря недопустимы в языке. Потому что, модифицируя код для case 2 вы на самом деле модифицируете его и для другого случая, т.е. код вводит в заблуждение. Возможно, это когда-то очень нужно (честно говоря, не представляю случая, когда такая необходимость была бы неохбодима), но её следует всеми силами избегать. У Макконнела конец 15-й главы посвящён этому вопросу.

          P.S. А второй пример в MSDN вообще ужасен. Вместо того, чтобы выделить цикл в отдельный метод и делать из него return, используют goto в место, которое даже не находится сразу после цикла.
            +2
            не представляю случая, когда такая необходимость была бы неохбодима

            Очень просто: когда один кейс представляет собой расширенный вариант другого. В этом случае можно сначала сделать дополнительную часть, а потом перейти к общей.
              0
              Под «необходима» я имел ввиду буквальное «нельзя обойтись без неё». Этот вариант очевиден, более того, он напрашивается. Но опять-таки, то, что очевидно в момент написания будет неочевидным при поддержке.
                +2
                Обычно такие вещи пишут в коде, который прямо перед глазами (более того, это тот случай, когда я жалею об отсутствии fall-through).
              +2
              не представляю случая, когда такая необходимость была бы неохбодима

              Типичный пример, когда это необходимо — конечный автомат. Наши кейсы в switch структуре будут состояниями, между которыми бы будем перескакивать в соответствии с логикой работы. Другое дело, что без goto можно и в этом случае обойтись — достаточно завернуть switch в while цикл, и устанавливать переменную для следующей итерации. Таким образом, goto case 2: превратится грубо говоря в variable = 2; break; и на следующей итерации мы туда и попадём. Я вот тоже не сторонник goto, этот оператор годится для несложных и «очевидных» случаев, а при разрастании кода обилие goto превратит код в спагетти, поэтому в сложных ситуациях лучше обойтись более продвинутыми паттернами (иногда используют итераторы, либо System.Activities.Statements.StateMachine, и т.д.).
                0
                Если у нас примитивный конечный автомат, который точно-точно не прибавит состояний, то возможно. И то, я бы выбрал табличный способ. А для более-менее сложных КА есть паттерн Состояние.
                0
                Это для вот таких случаев. Выделять здесь цикл в отдельный метод нельзя — его вызов будет некорректен в любом случае.
                            try{
                                myEvent1.Reset();
                                myEvent2.WaitOne();
                               
                                for (;;){
                                    for (;;){ goto labelExit;
                                    }
                                labelExit:;
                                }
                            }
                            finally{
                                myEvent1.Set();
                            }
                
                  0
                  Для вот таких вот случаев есть break; и goto тут не обязательно использовать. А вот если нужно выскочить сразу за пределы всех циклов, тогда да, можно и воспользоваться.
                0
                А почему нельзя так?
                switch(variable)
                {
                case 1:
                case 2:
                Console.WrileLine("case 1");
                Console.WrileLine("case 2");
                break;
                default:
                Console.WrileLine("default");
                break;
                }
                  +1
                  В целом, так можно, но не в данном случае: при case 2 будет выводить case1 + case2. Очевидно, что это не то, что хотелось бы.
                +2
                Объясните джависту:

                2,3. Почему так? У нас наоборот.
                7. А почему это вообще работает? Ведь в момент вызова делегата i уже не существует, мы ведь вышли из ее области видимости.
                  0
                  7. Тут важно разрушается ли переменная i в конце итерации, или переиспользуется. В данном случае переиспользуется. Чтобы исправить ситуацию достаточно либо завести новую переменную внутри цикла, либо использовать foreach (int i in Enumerable.Range(0, 10)) начиная с C# 4.
                    0
                    Я о другом. Почему [10, ..., 10], а не [0, ..., 9] — это понятно. Непонятно почему оно в текущем виде компилируется и работает, а не говорит что-то в духе «в момент вызова делегата переменной i уже не будет существовать, так что иди нахрен».

                    Другими словами, почему последнее i=10 сохраняется, а не разрушается.
                      +1
                      Ну потому что переменная i в таком случае будет находиться в куче, в инстансе специалного класса замыкания, и будет уничтожена только после GC.

                      Но любой нормальный статический анализатор подскажет, что здесь нечисто.
                        +1
                        Вот я и негодую что язык такое позволят. Ибо очевидно что что-то не так.
                          0
                          Наверное на то есть причина. Потому как у соседнего тут Go точно такая же «фича» — замыкание захватывает ссылку, GC доволен и получается тот же самый результат. Тоже фигурирует в такого рода списках, что нужно просто знать, чтобы потом не было неприятно.
                            0
                            А какие ещё опции? Как по-другому такой код написать?
                            Func<int> F(int i) { return () => i; }
                              0
                              Можете пояснить, что вы имеете ввиду? Неясный синтаксис.
                              Я так понимаю, вы имеете ввиду что-то вроде:

                              Func F = () => i;

                              Но это, по сути, то же самое, что и с делегатом выше.
                                0
                                Есть мысль, что захватывать можно только константы.
                                  0
                                  Есть мысль, что захватывать можно только константы.

                                  А смысл тогда их захватывать?
                                    0
                                    Эмн. Ну, например, для того, что написано в комментарии, на который я отвечал. Там, относительно замыкания, i — это константа.
                                      0
                                      Я не знаю, что такое «константа относительно замыкания», я отвечал именно относительно констант.
                                    0
                                    Идеологически оно может и верно, но практическая польза от замыканий при этом падает.
                                      0
                                      Ок, признаю, я действительно очень неудачно выбрал термин. Имел ввиду не static final из java или const из c++, а просто что-то неизменяемое в данной области видимости. Т.е. очевидно, что в вашем примере i — это переменная, и может принимать разные значения, но внутри метода F меняться значение не может. Так что мы можем создать замыкание и утащить туда конкретное значение.

                                      Возвращаясь к исходному примеру. Я бы понял, если бы на каждой итерации это были бы разные неизменяемые i. В таком случае, правильным был бы вывод [0, ..., 9]. Но вот в текущей реализации, я считаю, что создавать замыкание i (хз как правильно, «над i», «с i») нельзя. И следить за этим должен компилятор.
                                        0
                                        Слишком много магии, определение изменяемости/неизмениемости переменных. Предложение об изменении типа переменной в зависимости от скоупа if (a is B) { /* переменная a типа B */ } по этой же самой причине отклонили. Нельзя так просто взять и узнать, модифицируется ли переменная в скоупе, или нет. Вместо этого будет использоваться синтаксис if (a is B b), создающий новую переменную, что и рекомендуется делать, если вы запутались с замыканиями.

                                        Кстати, язык С++ даёт вам контроль над этим моментом, позволяя захват как по ссылке (как в нормальных языках), так и по значению (копируя данные).
                                          0
                                          1. Какой магии? Если компилятор может доказать что присваивание ровно одно — значит неизменяемая. Во всех остальных случаях считаем изменяемой. Например понятие «effectively final» из Java8: «A variable or parameter whose value is never changed after it is initialized is effectively final.». Еще ни разу не сталкивался с багами в этом месте. Естественно, я не говорю про C++, но в C#, на сколько я знаю, прямой работы с памятью нет, так что отследить изменения всегда должно быть возможно.

                                          2. Kotlin, Smart Casts.

                                          оффтоп
                                          Нельзя так просто взять и узнать

                                          Дурацкие мемы, автоматически в голове этот текст рисуется на соответствующем фоне.
                                            0
                                            Если компилятор может доказать что присваивание ровно одно — значит неизменяемая. Во всех остальных случаях считаем изменяемой.
                                            Если бы вы открыли статью, на которую я дал ссылку, вы бы увидели там объяснение, почему в C# это не так.

                                            Вкрадце, локальная переменная может находиться на куче, и там её значение может быть изменено по огромному количеству причин. (Обратите внимание на термины «переменная» и «значение» здесь — не путать с значимыми типами, они здесь роли не играют.)
                                              0
                                              Вы мне не ту часть скинули. В этой написано лишь (по данной теме) «программист и сам в состоянии уследить» (а с таким подходом недолго и до javascript докатиться). Проблемы изменяемости описаны в первой части. Ну так там и речь не про локальные переменные. По сути, про локалы остается лишь перегрузка методов из конца первой части. Там да, признаю, действительно проблема.

                                              Вообще, вижу что в C# с этим действительно сложнее чем в Java. А в котлиновских smart casts так вообще на ряд вещей забили.
                                          0
                                          внутри метода F меняться значение не может

                                          Вот только возвращенное замыкание существует и после того, как метод F перестал существовать. И если с int все просто, он был скопирован на входе в метод и с тех пор неизменен, то с reference type все намного сложнее.

                                          В реальности, правильная формулировка звучит как «замыкаться надо на immutable», как оно чаще всего в функциональных языках и делается, но в C# этого достичь не так-то просто.
                                            0
                                            А в референсах скопировали саму ссылку и окей)).

                                            На самом деле, я прекрасно понимаю что при изменением внутреннего состояния объекта по скопированной ссылки совершенно спокойно могут быть (и будут) проблемы, описанные в примере из статьи. Так что в самом правильном варианте — только immutable. Просто если введением local final variable можно убрать если не всю проблему, но хотя бы ее часть, то почему-бы этого не сделать?

                                            А вообще, для данной задачи понятие владения из Rust подходит даже больше чем immutable.
                                  0
                                  Вполне все логично и обяснимо.
                                  Захват происходит по ссылке. Очевидно что такая переменная выделятся уже не из стека (что характерно для структур), а из кучи и хранится там пока есть хотябы одна ссылка на эту переменную (в данном случае ссылка храниться в делегатах).
                                  Вообще нужно аккуратнее со всякими ref и замыканиями работать
                              0
                              В примерах 2 и 3 в C# так хотя бы потому, что эти методы не виртуальные по умолчанию как в Java. К слову, если попытаться сделать виртуальными, будет ошибка вроде «main.cs(22,30): error CS0115: `Program.B.abc(Program.P)' is marked as an override but no suitable method found to override». C++ себя ведёт так же.
                              Вопрос в том, почему Java так странно работает, если сделать аннотацию @ Override, то будет ошибка компиляции как в C# (error: method does not override or implement a method from a supertype), иными слова метод наследника не переопределялся, но при этом Java лезет вверх по наследованию для подходящего метода. Чтобы результат был как в C# (abc из B) надо метод базового класса сделать private.
                              Самое весёлое, что такой код из задачи никто и никогда не стал бы писать.
                                +4
                                Не соглашусь, в java как раз все логично (по крайней мере, в данном случае). Метод определяется не только по имени, но и по типам параметров. Соответственно abc(int) и abc(double) — это разные методы. Потому и не переопределяется. Вот если в наследнике тоже сделать abc(int), то будет честное переопределение и напечатается B.

                                надо метод базового класса сделать private

                                Ну тогда он на результат вообще никак влиять не будет. Независимо от класса B. Так что явно плохое исправление.

                                Самое весёлое, что такой код из задачи никто и никогда не стал бы писать.

                                Безусловно. Просто хотелось понять непонятный результат.
                                  +8
                                  Вообще при поиске метода, должна учитываться полная сигнатура, и в данном случае у класса B есть две публичных перегрузки, abc(int) и abc(double). Почему компилятор выбирает вторую для вызова при наличии подходящей сигнатуры abc(int), очень хороший вопрос, и стоит пожалуй с ним подробнее разобраться. Виртуальность здесь совершенно не причем.
                                    +2
                                    А вот и пояснения странному поведению вопроса 2: http://csharpindepth.com/Articles/General/Overloading.aspx
                                      +2
                                      А вот то, что даже при переопределении метода с параметром int вызывается всё равно с double — вообще не круто =\
                                        0
                                        Благодарю, очень интересный нюанс.
                                    0
                                    по поводу номера 2
                                    На С++ тоже так. Честно говоря, я даже и не знал о таком, ибо никогда так не напишу… но получается что при выборе метода компилятор считает что double «наследуется» от int ???
                                      0
                                      Не наследуется, в данном случае используется неявное приведение типа, т.к. операция double a = (int)5 валидна, она же используется при вызове метода.
                                    0
                                    Вот еще боян :)

                                    static void Main(string[] args)
                                            {
                                                double x = secretFunction();
                                                if (x != x)
                                                {
                                                        Console.WriteLine("Вот так вот :D");
                                                }
                                                Console.ReadLine();
                                            }
                                    


                                    Выводит «Вот так вот :D»

                                    Привести пример secretFunction.

                                    Ответ
                                    public static double secretFunction()
                                    {
                                        return 0.0/0.0;
                                    }
                                    

                                      +1
                                      Или
                                      Ответ
                                      Более явно:
                                      public static double secretFunction()
                                      {
                                          return double.NaN;
                                      }
                                      

                                        –3
                                        ну это вопрос в вопросе для подсмотревших, понять, как оно такое вышло) а ваш вариант интригу убивает
                                          +4
                                          Извините, не думал что это специально. Я бы сразу выдал подробный ответ.
                                      +8
                                      Ну уж если быть точным то в 1

                                      1) «Обе переменные не инициализированы», это ложное утверждение, поля класса инициализируются в значения по умолчанию (они же нули) при создании экземпляра, или в данном случае при создании экземпляра типа.

                                      2) «если быть более точным, то это immutable тип, что означает reference тип с семантикой value типа», в данном контексте тоже бред, да и вообще бред, что значит с семантикой value типа? Значение локальных переменных области видимости, хранится на стеке? нет! В методы передается копия? Нет!
                                        +3
                                        Хочу еще вопросов, разных, красных синих, и малиновых. Довольно занимательные задачи.
                                        Было бы хорошо если бы было объяснение для каждого…
                                          +3
                                          Console.ReadLine();


                                          Зачем вы везде это пишете?
                                            0
                                            Чтобы консоль не закрывалась и можно было посмотреть результат.
                                              +2
                                              Ctrl-F5 же.
                                                +2
                                                Слушай, ну я знаю про Ctrl-F5, но спрашивали же зачем ридлайн. Я объяснил зачем он по моему мнению.
                                                0
                                                я буду всегда обновлять комментарии, перед тем как их писать.
                                                  –3
                                                  http://slonopotamus.org/console.png

                                                  Ничего не закрывается. Что я делаю не так?
                                                0
                                                > float и double не являются встроенными типами данных (integral types)

                                                такой перевод (или его применение) коробит
                                                float и double настолько же встроенные типы как и int
                                                лучше integral types так и называть «интегральные типы» или целые (включая char & bool)
                                                https://msdn.microsoft.com/ru-ru/library/ya5y69ds.aspx

                                                P.S. интегральные + числа с плавающей точкой = арифметические
                                                +6
                                                Поразвлекаться неплохо, но если вы реально принимаете решение брать или не брать человека на основе этих вопросов, то это ппц. Особенно эта каша из классов A, B, C с наследованием.
                                                Есть основы, которые надо знать (типа отличния абстстрактного класса и интерфейса), а есть попытки заставить кандидата компиллировать говнокод в голове.
                                                  +2
                                                  Я в таких случаях в ответ спрашиваю правда ли такой код встречается в репозитории. И если да, то для меня это сигнал не идти сюда.
                                                    0
                                                    Дело не в коде, а в понимании того, как работает тот инструмент который вы используете, без понимания даже следуя лучшим практикам можно сделать такое интересное решение.
                                                      +1
                                                      Скорее процент правильных ответов это очень косвенный признак того, сколько лет автор провёл, читая код самых разных проектов. Что, конечно, приведёт к печальному результату собеседования на Junior программиста.

                                                      Хотя… с практической точки зрения это можно использовать чтобы деморализовать претендента и заставить его подписать трудовой договор на меньшую з/п.
                                                        +1
                                                        Так и не понял, при чем тут код разных проектов? Я меня не так много проектов, которые я реально читал, почти все задачи для меня понятны. А не понятные в свою очередь интересны, вот вы например знали почему в 2 и 3 получается такой результат? Я не знал и не жалею, что потратил время, на то что бы разобраться.
                                                          0
                                                          Так и не понял, при чем тут код разных проектов?
                                                          Чтобы слёту ответить на все вопросы, на мой взгляд, нужно иметь опыт, уже набив шишки на подобных чьих-то решениях в чужих проектах. Так как вопросы на мой взгляд не совсем тривиальные и даже сомнительные (в плане целесообразности использования), может понадобиться много времени, чтобы повстречать их ВСЕ в тех или иных реальных проектах. Почему в проектах и почему реальных? Потому что лично я не думаю, что можно заполучить соответствующие знания, лишь просто прочитав несколько книг.
                                                            0
                                                            Знания достаточные для правильного ответа на все задачи можно получить из одной книги C# Spec
                                                    0
                                                    Не соглашусь, вот например задачка 2 ну и 3 за одно, очень интересной оказалась и реально может устроить в реальной жизни сюрприз.
                                                    0
                                                    А теперь более каверзный вопрос на тему вопроса номер 7.
                                                    delegate void SomeMethod();
                                                    
                                                    static void Main(string[] args)
                                                    {
                                                                List<SomeMethod> delList = new List<SomeMethod>();
                                                                foreach (int i in System.Linq.Enumerable.Range(1, 10))
                                                                {
                                                                    delList.Add(delegate { Console.WriteLine(i); });
                                                                }
                                                    
                                                                foreach (var del in delList)
                                                                {
                                                                    del();
                                                                }
                                                    }
                                                    


                                                    Ответ
                                                    Правильный ответ: какую версию C# вы используете? При C# >= 5: будут выведены числа от 1 до 10, для более старых — десять десяток.
                                                    Да, это breaking changes, можно почитать stackoverflow.com/questions/12112881/has-foreachs-use-of-variables-been-changed-in-c-sharp-5
                                                      +3
                                                      А теперь поясните зачем нормальному человеку в 2016 пользоваться delegate, когда есть
                                                      Func<T>
                                                      и прочие.
                                                        +1
                                                        Если вне контекста задачи — то для событий, например. А в контексте — ну делегат и делегат, от замены на Action/Func концептуально ничего не поменяется.
                                                          0
                                                          Если честно, никогда не сталкивался с этой проблемой в реальной жизни. Зато каждый, кто прочитал про closure, норовит сунуть этот пример в интервью (обязательно с циклом for).
                                                          Я не спорю что знать полезно. Просто покажите мне человека, который столкнулся с этим в реальном проекте.
                                                            0
                                                            Я-таки сталкивался, но с вами абсолютно согласен. Большинство задач (да и практик) «хитрого кода» — от лукавого.
                                                          0
                                                          Я просто написал пример по образу и подобию примера в статье, так-то там лямбду надо использовать и Action.
                                                          0
                                                          А вот такой код выдаст 1 2 3 на любой версии языка
                                                           static void Main(string[] args)
                                                                  {
                                                                      var delList = Enumerable.Range(1, 3)
                                                                          .Select(i => (Action) delegate { Console.WriteLine(i); });
                                                          
                                                                      foreach (var del in delList )
                                                                      {
                                                                          del();
                                                                      }
                                                                  }
                                                          


                                                          Ибо Linq ленив и замыкание здесь будет стряпаться прямо перед выполнением
                                                            0
                                                            Так, а я вот даже не поленился, сменил версию языка на C# 4.0, и для верности даже версию фреймворка на 3.5, но foreach в лбом варианте упорно выдает 1 2 3
                                                            Что-то тут нечисто. Сижу в 2015 студии
                                                              0
                                                              2015 студия использует компилятор Roslyn даже для предыдущих версий языков и фреймворков.
                                                              0
                                                              Тут, вроде, нет замыкания. Просто i передаётся как параметр в функцию, где благополучно копируется по значению и выводится.
                                                                0
                                                                Есть.
                                                                  0
                                                                  Здесь нет замыкания, ну прям совсем нет.
                                                                    0
                                                                    Ну я даже не знаю, можете декомпилировать и проверить. В .NET нет инструментов, чтобы оптимизировать этот C# код с замыканием таким образом, чтобы он выполнялся без замыкания.
                                                                      0
                                                                      Где тут замыкание?

                                                                      var delList = Enumerable.Range(1, 3).Select(i => (Action) delegate { Console.WriteLine(i); });
                                                                        0
                                                                        Создаётся через ключевое слово delegate.
                                                                          0
                                                                          Да действительно я ступил, тут есть замыкание, но когда происходит захват контекста в данном случае i, мы имеем захват контекста вызова i =>… что приводит к тому, что замыкание каждый раз захватывает не внешнюю переменную, а значение i на момент вызова, т.е. по факту происходит захват значения 1, 2, 3, 4, а не захват внешней переменной.

                                                                          var smth = 0;
                                                                          var delList = Enumerable.Range(1, 3).Select(i => { smth = i; return (Action) delegate { Console.WriteLine(smth); }; });
                                                                            0
                                                                            Ну еще .ToList() надо для порядка иначе отображение совпадет с передачей значения.
                                                                              0
                                                                              Да, я конечно же говорю про вариант с .ToList(), он тоже выдает 1 2 3. И в 2013 студии, кстати тоже 1 2 3
                                                                                0
                                                                                var smth = 0;
                                                                                var delList = Enumerable.Range(1, 3).Select(i => { smth = i; return (Action) delegate { Console.WriteLine(smth); }; }).ToList();

                                                                                Выдает 3 3 3 т.к. происходит захват переменной smth. А ее значение к моменту вызова делегата с замыканием уже 3 и не меняется, что собственно происходит в примере приведенном в статье.

                                                          Only users with full accounts can post comments. Log in, please.