← Academy Blog

Марафон підготовки 2021

Стартував марафон підготовки до тестів - .NET трек - Binary Studio Academy 2021! Щотижневі питання з відповідями та пояснення чекають на нашому блозі.

  • [.NET] Який результат виконання програми?
    static void Main(string[] args)
    {
      Console.Write(GetValue());
    }
    
    public static string GetValue()
    {
      try
      {
        throw new Exception("oops");
      }
      catch (Exception)
      {
        Console.Write("catch ");
        return "return ";
      }
      finally
      {
        Console.Write("finally ");
      }
    }

    a) catch return finally
    b) catch finally return
    c) finally catch return
    d) catch return

    Розгорнути правильну відповідь з поясненням

    Правильна відповідь: b) catch finally return

    Щоб відповісти на це питання, давайте спочатку заглибимось у виключення і як їх обробляти на платформі .NET. Виключення - це помилка, яка генерується при виконанні програми внаслідок непередбачуваної поведінки. Якщо таку помилку не обробити, то наша програма завершить своє виконання. Саме для цього мова C# надає розробникам таку конструкцію, як try-catch-finally блоки. Де try блок - це та ділянка коду, яка на нашу думку є ненадійною і в нас є план дій, як усунути негативні наслідки в разі виникнення непередбачуваної ситуації в ній. Цей план дій описується в catch блоках, при чому їх може бути як декілька, так і взагалі не бути об’явленим. Декілька catch блоків описують для того, аби можна було більш точно обробити помилку, наприклад помилки різних типів (ArgumentException, InvalidOperationException і т.д), або накласти додаткову умову через вираз when, який вказується зразу після catch виразу. І останній блок - це опціональний блок finally, який виконується зразу після обробки виключення у блоці catch, і він може бути вказаним тільки один раз. Зазвичай finally блок застосовується для звільнення ресурсів, таких як закриття файлу.

    Давайте спробуємо відтворити порядок дій програми, взявши до уваги все, що було описано вище. Спочатку викликається метод GetValue, результат виконання якого буде виведено в консоль за допомогою Console.Write. При виконанні GetValue метода одразу ж генерується виключення, але оскільки код, що генерує виключення, обернений в try блок, то виконання переходить в catch, де виводиться стрічка “catch ” і повертається “return ”. Але не забуваймо, що в нас також є finally, який виконується зразу після catch блоку. Тому результат повернення зберігається і буде повернуто до місця виклику лише після того, як відпрацює finally, тобто коли виведеться стрічка “finally ”. Склавши всі ці кроки докупи, отримаємо “catch finally return”.

  • [.NET] Який рядок потрібно розкоментувати для того, щоб отримати Hello World?
    public abstract class BaseClass
    {
      public virtual void Hello()
      {
        Console.Write("Hello");
        //Console.WriteLine(" World!"); // 1
      }
    }
    
    public class ChildClass : BaseClass
    {
      public override void Hello()
      {
        //this.Hello(); // 2
        //base.Hello(); // 3
        Console.WriteLine(" World!");
      }
    }
    
    class Program
    {
      static void Main(string[] args)
      {
        BaseClass item = new ChildClass();
        item.Hello();
        Console.ReadLine();
      }
    }
    

    a) 1
    b) 2
    c) 3
    d) Hello World! і так виводиться

    Розгорнути правильну відповідь з поясненням

    Правильна відповідь: c) 3

    Щоб правильно відповісти на це питання потрібно згадати, що таке поліморфізм і як він працює. Почитати про це можна тут.

    Перейдемо до завдання. В першому рядку метода Main створюється екземпляр класу ChildClass, і одразу ж виконується неявне приведення його до базового класу BaseClass, тобто виконується upcasting. Хоча в нас посилання є BaseClass типу, але все одно воно вказує на ту ділянку в пам’яті, де було створено екземпляр класу ChildClass. Тому при виклику функції Hello в другому рядку викликається перевизначена версія методу з класу ChildClass, тобто варіанти 1 і 4 вже відпадають. Якщо розкоментувати рядок 2, то відбудеться вічна рекурсія, яка завершиться помилкою переповненого стеку. Залишається варіант 3, який і є правильним. В цьому випадку спочатку виконається реалізація базового класу, що виводить стрічку “Hello”, і після цього виведеться " World!" з наявної реалізації.