Вопрос – Ответ
February 19, 2019

Вопросы на собеседованиях по C# и .Net. На позицию junior/middle. Часть 6.


!!НАШ БЛОГ ПЕРЕЕХАЛ!!

Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда.

Наш новый сайт maddevelop.ru

А данную статью вы можете найти по ссылке ниже:

https://maddevelop.ru/Textbook/ViewSelectedArticle?textbookName=C%23&sectionName=%D0%A1%D0%BE%D0%B1%D0%B5%D1%81%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20%D0%BF%D0%BE%20C%23%20%D0%B8%20.NET&subsectionName=%D0%A1%D0%BE%D0%B1%D0%B5%D1%81%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20%D0%BD%D0%B0%20%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D1%8E%20junior%2Fmiddle&articleName=%236%20%D0%91%D0%BB%D0%BE%D0%BA%20%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81%D0%BE%D0%B2%20(%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81%D1%8B%2042-56)

P.S. движок teletype.in очень странно интерпретирует ссылку, и пришлось вставлять ее полным текстом


Вопрос 42

В чем разница инкапсуляции и сокрытия?

Ответ: Инкапсуляция - одна из парадигм ООП. Она представляет собой способность языка упаковывать определённые участки кода в контейнеры, исключая возможность внешнего мира нарушения целостности данного кода. Основной единицей инкапсуляции в C# является класс. Инкапсуляция позволяет структурировать код и помогает обезопасить его от многих возможных проблем, относительно защиты данных и информации.

Сокрытие же скрывает детали о процессе. Для определения прав доступа к данным в классе и к классу непосредственно используются модификаторы доступа. Получается, что использование этих модификаторов и есть то самое сокрытие.

Но сам термин "сокрытие" лучше употреблять в контексте методов. Сокрытие метода представляет собой реализация тела метода в дочернем классе, сигнатура которого соответствует сигнатуре метода в родительском классе. Для сокрытия применяется ключевое слово "new".

Пример.

class Animal
{
    public void Say()
    {
        Console.WriteLine("*Some sounds*");
    }
}
class Cat:Animal
{
    public new void Say()// the usage of hiding technique
    {
        Console.WriteLine("Miew!");
    }
}

Лучше термин "сокрытие" использовать в контексте именно сокрытия метода, потому что нет определённого правила отличия инкапсуляции и сокрытия, и на этом фоне могут плявляться разные споры.

Вопрос 43

Что такое частные и общие сборки?

Ответ: Частные сборки:

  • Видны только самому приложению
  • Нет необходимости заботиться об уникальном имени во всем глобальном пространстве имен
  • Не нужно делать записей в реестре при развертывании приложения
  • Сборки просто копируются в директорию приложения или в подчинённую директорию
  • Общая среда выполнения (CLR) при запуске приложения прочитает его манифест и определит какие сборки необходимы. Затем будет произведен поиск нужной сборки по директории приложения (процесс зондирования)

Общие сборки:

  • Общие сборки могут быть использованы сразу несколькими приложениями
  • Сборка должна иметь строгое имя (strong name)
  • Сборка должна быть помещена в общедоступное место – Global Assembly Cache (GAC, глобальный кэш сборок)

Вопрос 44

Что такое .Net Framework?

Ответ: .NET Framework — программная платформа, выпущенная компанией Microsoft в 2002 году. Основой платформы является общеязыковая среда исполнения Common Language Runtime (CLR), которая подходит для разных языков программирования. Функциональные возможности CLR доступны в любых языках программирования, использующих эту среду.

(Источник Википедия)

Вопрос 45

Чем управляемый код (managed code) отличается от неуправляемого (unmanaged code)?

Ответ: Управля́емый код (managed code) — термин, введённый фирмой Microsoft, для обозначения кода программы, исполняемой под «управлением» виртуальной машины .NET — Common Language Runtime или Mono. При этом машинный код называется неуправля́емым кодом (unmanaged code).

Слово «управляемый» относится к методу обмена информацией между программой и исполняющей средой. Оно означает, что в любой точке исполнения управляющая среда может приостановить исполнение и получить информацию, специфичную для текущего состояния. Необходимая для этого информация представлена в управляемом коде на языке Intermediate Language и в связанных с этим кодом метаданных.

С формальной точки зрения управляемым кодом является любой программный код, исполняемый в среде отладчика.

(Источник Викидедия)

Вопрос 46

LINQ lazy loading, eager loading в чем разница

Ответ:

В случае lazy loading, зависимые таблицы (дочерние объекты) не загружаются автоматически с родительскими, а загрузятся в тот момент, когда они понадобятся. В LINQ по умолчанию используется lazy loading.

Например:

var query = context.Categories.Take(3); // Lazy loading
 
foreach (var Category in query)
{
   Console.WriteLine(Category.Name);
   foreach (var Product in Category.Products)
   {
   Console.WriteLine(Product.ProductID);
   }
}

Таким образом сгенерируется следующий SQL код:

SELECT TOP (3) 
[c].[CatID] AS [CatID], 
[c].[Name] AS [Name], 
[c].[CreatedDate] AS [CreatedDate]
FROM [dbo].[Category] AS [c]
GO
 
-- Region Parameters
DECLARE @EntityKeyValue1 Int = 1
-- EndRegion
SELECT 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[Name] AS [Name], 
[Extent1].[UnitPrice] AS [UnitPrice], 
[Extent1].[CatID] AS [CatID], 
[Extent1].[EntryDate] AS [EntryDate], 
[Extent1].[ExpiryDate] AS [ExpiryDate]
FROM [dbo].[Product] AS [Extent1]
WHERE [Extent1].[CatID] = @EntityKeyValue1
GO
 
-- Region Parameters
DECLARE @EntityKeyValue1 Int = 2
-- EndRegion
SELECT 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[Name] AS [Name], 
[Extent1].[UnitPrice] AS [UnitPrice], 
[Extent1].[CatID] AS [CatID], 
[Extent1].[EntryDate] AS [EntryDate], 
[Extent1].[ExpiryDate] AS [ExpiryDate]
FROM [dbo].[Product] AS [Extent1]
WHERE [Extent1].[CatID] = @EntityKeyValue1
GO
 
-- Region Parameters
DECLARE @EntityKeyValue1 Int = 3
-- EndRegion
SELECT 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[Name] AS [Name], 
[Extent1].[UnitPrice] AS [UnitPrice], 
[Extent1].[CatID] AS [CatID], 
[Extent1].[EntryDate] AS [EntryDate], 
[Extent1].[ExpiryDate] AS [ExpiryDate]
FROM [dbo].[Product] AS [Extent1]
WHERE [Extent1].[CatID] = @EntityKeyValue1

Как видно из примера, мы получаем 4 SQL запроса. Это означает, что будет происходить 4 обращения к базе данных, один раз для таблицы Categories и 3 раза для таблицы Products, которая связана с Categories. В этом случае дочерние объекты заполняются по запросу.

Можно отключить lazy loading. Для этого нужно установить параметр LazyLoadingEnabled свойства ContextOptions объекта контекста в состояние false. После этого можно будет получать зависимые от родителя объекты при единственном запросе.

context.ContextOptions.LazyLoadingEnabled = false;

В случае eager loading, зависимые объекты загружаются автоматически с родительской таблицей. Для того, чтобы использовать eager loading нужно применить метод Include().

Например:

var query = context.Categories.Include("Products").Take(3); // Eager loading
 
 foreach (var Category in query)
 {
 Console.WriteLine(Category.Name);
 foreach (var Product in Category.Products)
 {
 Console.WriteLine(Product.ProductID);
 }
 }

Сгенерированный SQL запрос:

SELECT [Project1].[CatID] AS [CatID], 
 [Project1].[Name] AS [Name], 
 [Project1].[CreatedDate] AS [CreatedDate], 
 [Project1].[C1] AS [C1], 
 [Project1].[ProductID] AS [ProductID], 
 [Project1].[Name1] AS [Name1], 
 [Project1].[UnitPrice] AS [UnitPrice], 
 [Project1].[CatID1] AS [CatID1], 
 [Project1].[EntryDate] AS [EntryDate], 
 [Project1].[ExpiryDate] AS [ExpiryDate]
 FROM (SELECT 
 [Limit1].[CatID] AS [CatID], 
 [Limit1].[Name] AS [Name], 
 [Limit1].[CreatedDate] AS [CreatedDate], 
 [Extent2].[ProductID] AS [ProductID], 
 [Extent2].[Name] AS [Name1], 
 [Extent2].[UnitPrice] AS [UnitPrice], 
 [Extent2].[CatID] AS [CatID1], 
 [Extent2].[EntryDate] AS [EntryDate], 
 [Extent2].[ExpiryDate] AS [ExpiryDate], 
 CASE WHEN ([Extent2].[ProductID] IS NULL) THEN CAST(NULL AS int) 
ELSE 1 END AS [C1]
FROM (SELECT TOP (3) [c].[CatID] AS [CatID], [c].[Name] AS [Name], [c].[CreatedDate] AS [CreatedDate]
 FROM [dbo].[Category] AS [c] ) 
AS [Limit1]
 LEFT OUTER JOIN [dbo].[Product] AS [Extent2] 
ON [Limit1].[CatID] = [Extent2].[CatID]) AS [Project1]
 ORDER BY [Project1].[CatID] ASC, [Project1].[C1] ASC

Как можно заметить имеется только один SQL запрос. А это означает, что обращение к базе данных будет происходить только единожды для таблицы Categories и Products зависящей от нее. Таким образом, полученная выборка будет сразу содержать данные от родительской и от дочерней таблицы.

(Перевод статьи)

Вопрос 47

Можно ли запретить наследование от своего собственного класса?

Ответ:

Для того, чтобы запретить наследоваться от класса необходимо объявить его с модификатором sealed.

Например:

sealed class SomeClass
{
    // Объявление класса
}


Вопрос 48

Можно ли разрешить наследование класса, но запретить переопределение метода?

Ответ:

Да. Для этого нужно определить родительский класс с модификатором public, а метод в нем пометить как sealed.

Например:

class Base {
   public virtual void Test() { ... }
}
class Subclass1 : Base {
   public sealed override void Test() { ... }
}
class Subclass2 : Subclass1 {
   public override void Test() { ... } // Не скомпилируется!
}

Вопрос 49

Определение паттерна синглтон

Ответ:

Одиночка (Singleton, Синглтон) - порождающий паттерн, который гарантирует, что для определенного класса будет создан только один объект, а также предоставит к этому объекту точку доступа. Используется тогда, когда необходимо, чтобы для класса существовал только один экземпляр. Он позволяет создать объект только при его необходимости. Если объект не нужен, то он не будет создан. В этом отличие синглтона от глобальных переменных.

Классическая реализация данного шаблона на C#:

class Singleton
{
    private static Singleton instance;
 
    private Singleton()
    {}
 
    public static Singleton getInstance()
    {
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
}

В классе определяется статическая переменная - ссылка на конкретный экземпляр данного объекта и приватный конструктор. В статическом методеgetInstance() этот конструктор вызывается для создания объекта, если, конечно, объект отсутствует и равен null.

(Источник)

Вопрос 50

Thread, task, примеры использования?

Ответ:

Класс Thread, позволяет выделить, которые будут выполняться одновременно.

Для запуска нового потока нужно определить метод, который будет выполняться в потоке. Для создания потока используется делегат ThreadStart, получающий в качестве параметра определенный ранее метод.

Для запуска потока вызывается метод Start.

using System.Threading;
 
class Program
{
    static void Main(string[] args)
    {
        // создаем новый поток
        Thread myThread = new Thread(new ThreadStart(Count));
        myThread.Start(); // запускаем поток
 
        for (int i = 1; i < 9; i++)
        {
            Console.WriteLine("Главный поток:");
            Console.WriteLine(i * i);
            Thread.Sleep(300);
        }
 
        Console.ReadLine();
    }
 
    public static void Count()
    {
        for (int i = 1; i < 9; i++)
        {
            Console.WriteLine("Второй поток:");
            Console.WriteLine(i * i);
            Thread.Sleep(400);
        }
    }
}

Новый поток будет производить действия, определенные в методе Count. Для запуска этого метода в качестве второго потока, создается объект потока: Thread myThread = new Thread(new ThreadStart(Count));. В конструктор передается делегат ThreadStart, который в качестве параметра принимает метод Count. И следующим методом myThread.Start() запускается поток. После этого управление передается главному потоку, и выполняются все остальные действия, определенные в методе Main.

Существует еще одна форма создания потока: Thread myThread = new Thread(Count); Хотя в данном случае явным образом мы не используем делегат ThreadStart, но неявно он создается. Компилятор C# выводит делегат из сигнатуры метода Count и вызывает соответствующий конструктор.

(Источник)

Класс Task, который находится в пространстве имен System.Threading.Tasks, позволяет запускать отдельную продолжительную задачу. Она запускается асинхронно в одном из потоков из пула потоков, но ее можно запускать и синхронно.

Первый способ запуска - это создание объекта Task и вызов у него метода Start:

Task task = new Task(() => Console.WriteLine("Hello Task!"));
task.Start();

В качестве параметра объект Task принимает делегат Action. А метод Start() запускает задачу.

Второй способ - это использование статического метода Task.Factory.StartNew(). Он в качестве параметра принимает делегат Action. При этом этот метод сразу же запускает задачу:

Task task = Task.Factory.StartNew(() => Console.WriteLine("Hello Task!"));

В качестве результата метод возвращает запущенную задачу.

Третий способ определения и запуска задач представляет использование статического метода Task.Run():

Task task = Task.Run(() => Console.WriteLine("Hello Task!"));

Метод Task.Run() также в качестве параметра может принимать делегат Action и возвращает объект Task.

Пример:

using System;
using System.Threading.Tasks;
 
namespace HelloApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Task task1 = new Task(() => Console.WriteLine("Task1 is executed"));
            task1.Start();
 
            Task task2 = Task.Factory.StartNew(() => Console.WriteLine("Task2 is executed"));
 
            Task task3 = Task.Run(() => Console.WriteLine("Task3 is executed"));
             
            Console.ReadLine();
        }
    }
}

(Источник)

Вопрос 51

Что такое интеграционные тесты и unit-тесты?

Ответ:

  • Unit тест: Очень специфичны. Тестирование производится над одним классом, либо над конкретным методом класса. Должна быть четко поставленная задача, чтобы проверить правильность работы элемента программы. Сложные зависимости и взаимодействия не тестируются.
  • Интеграционный тест: проверяет правильность взаимодействия нескольких подсистем. Существует целый спектр вариантов, от тестирования взаимодействия между двумя классами до тестирования взаимодействия с программной средой.

Вопрос 52

Что такое MVVM?

Ответ: Паттерн MVVM (Model-View-ViewModel) позволяет отделить логику приложения от визуальной части. Он является архитектурным, (задает общую архитектуру приложения).

Паттерн был представлен Джоном Госсманом в 2005 году как модификация шаблона Presentation Model и был первоначально нацелен на разработку приложений в WPF. И хотя сейчас данный паттерн вышел за пределы WPF и применяется в самых различных технологиях, в том числе при разработке под Android, iOS, тем не менее WPF является довольно показательной технологией, которая раскрывает возможности данного паттерна.

MVVM состоит из трех компонентов: модели (Model), модели представления (ViewModel) и представления (View).

Вопрос 53

Что будет выведено в результате выполнения программы?

class Program
{
    private enum En
    {
        First = 15,
        Second,
        Third = 54
    }
    static void Main(string[] args)
    {
        Console.WriteLine((int)En.Second);
        Console.Read();
    }
}

Варианты ответов:

  1. 0
  2. 1
  3. 16
  4. Возникнет ошибка на этапе компиляции

Ответ: В результате выведется число 16 (ответ 3).

Потому, что при создании типа перечисления каждому элементу целочисленное значение, причем первый элемент будет иметь значение 0, второй - 1 и так далее. Первому элементу явно присваивается значение 15, а значит второму будет присвоено соответственно 16.

Вопрос 54

Когда использовать StringBuilder предпочтительнее, чем string:

  1. Если строка редко изменяется
  2. Если строка часто изменяется
  3. Если строка содержит спецсимволы
  4. Если строка содержит исключительно цифры

Ответ:

StringBuilder предпочтительнее использовать если строка часто изменяется (ответ 2).

А в более развернутом варианте можно ответить так:

Microsoft рекомендует использовать класс String в следующих случаях:

  • При небольшом количестве операций и изменений над строками
  • При выполнении фиксированного количества операций объединения. В этом случае компилятор может объединить все операции объединения в одну
  • Когда надо выполнять масштабные операции поиска при построении строки, например IndexOf или StartsWith. Класс StringBuilder не имеет подобных методов.

Класс StringBuilder рекомендуется использовать в следующих случаях:

  • При неизвестном количестве операций и изменений над строками во время выполнения программы
  • Когда предполагается, что приложению придется сделать множество подобных операций

Вопрос 55

Что будет выведено в результате выполнения программы?

class Program
{
    static void Main(string[] args)
    {
        int c = 3;
        Console.Write(Sum(5,3,out c)+" ");
        Console.Write(c);
        Console.ReadLine();
    }
    static int Sum(int a, int b, out int c)
    {
        return a + b;
    }
}

Варианты ответов:

  1. 8
  2. 8 3
  3. 8 0
  4. Возникнет ошибка на этапе компиляции

Ответ: Возникнет ошибка на этапе компиляции. (ответ 4) Это произойдет в следствии того, что параметру "с" метода Sum, помеченному ключевым словом out, не присвоено значение в теле метода, а это противоречит условию использования модификатора out. Ключевое слово out нужно для того, чтобы сделать параметр "выходным". Результат метода будет получен не только через return, но и через этот параметр "с".

Вопрос 56

Что будет выведено в результате выполнения программы?

class Program
{
    static void Main(string[] args)
    {
        var a = null;
        a = 10;
        Console.WriteLine(a);
        Console.ReadLine();
    }
}

Варианты ответов:

  1. 0
  2. 10
  3. Пустая строка
  4. Возникнет ошибка на этапе компиляции

Ответ: Возникнет ошибка на этапе компиляции (Ответ 4) Причина в том что нельзя присваивать значение null к неявно типизированной переменной.



Ещё больше интересной информации на нашем Telegram канале.

<< К части 5 << ......... >> К части 7 >>

Источник вопросов:

Metanit. Собеседование по C#. Часть 5