Прочее
January 11, 2019

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

Совсем скоро настанет момент, когда нужно будет устраиваться на другую работу, а, соответственно, начнутся и собеседования. Для того чтобы не облажаться получить должность, желательно подготовиться. Поэтому появилась идея создать серию статей, в которых будут собраны различные вопросы, которые могут Вам попасться, и дать на них ответы. Материалы брались из различных источников, список будет в конце. Предлагайте в комментариях свои варианты вопросов, они так же будут добавлены в общий список. Итак, начнем!


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

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

Наш новый сайт 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=%231%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%201-5)

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


Вопрос 1.

Есть следующее объявление классов A и B:

class A
{
	virtual void Foo()
	{
		Console.Write("Class A");
	}
}
class B: A
{
	override void Foo()
	{
		Console.Write("Class B");
	}
}

Что выведут на консоль такие вызовы метода Foo():

B obj1 = new A();
obj.Foo();

B obj2 = new B();
obj2.Foo();

A obj3 = new B();
obj3.Foo();

Ответ:

Во-первых ошибка в том, что не указаны модификаторы доступа в определениях метода Foo в классах A и B.

Нельзя использовать модификатор virtual с Модификаторами staticabstractprivate, или override. (ссылка на источник: https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/keywords/virtual )

А по умолчанию при определении метода ему присваивается модификатор private.

Во-вторых когда создается переменная obj1 типа B и в нее помещается объект типа A

B obj1 = new A();

производится неявный downcast, что недопустимо.

Правильный код бы выглядел вот так

class A
{
	public virtual void Foo()
	{
		Console.Write("Class A");
	}
}
class B: A
{
	public override void Foo()
	{
		Console.Write("Class B");
	}
}
class Program
{
  static void Main()
  {   
    B obj2 = new B();
    obj2.Foo();
    
    A obj3 = new B();
    obj3.Foo();
  } 
}

Тогда результатом будет:

B
B

Вопрос 2.

Есть следующая структура:

public struct S : IDisposable
{
    private bool dispose;
    public void Dispose()
    {
        dispose = true;
    }
    public bool GetDispose()
    {
        return dispose;
    }
}

Что будет выведено в следующем случае:

var s = new S();
using (s)
{
    Console.WriteLine(s.GetDispose());
}
Console.WriteLine(s.GetDispose());

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

  1. true, true
  2. true, false
  3. false, true
  4. false, false

Ответ:

Правильный ответ под номером 4 (false, false). Это происходит потому, что блок

using (s)
{
	...
}

на самом деле выполняется как

using (S s2 = s)
{
	...
}

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

На эту тему есть статья. (скорее всего сделаю перевод этой статьи и опубликую здесь, уж очень она познавательна)

Вопрос 3.

Есть следующий код:

List<Action> actions = new List<Action>();
for(var count=0; count<10; count++)
{
    actions.Add(() => Console.WriteLine(count));
}
foreach(var action in actions)
{
    action();
}

Что будет выведено на консоль? Варианты ответов:

  1. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
  2. 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
  3. Код сгенерирует исключение
  4. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

Ответ:

Правильный ответ 2 (выведет 10 раз число 10).

Это происходит потому, что в цикле for в переменную actions будет записано 10 действий WriteLine(count). После 10 итерации цикла for переменная count примет значение 10. А когда мы будем пробегаться по циклу foreach, мы будем вызывать подряд 10 методов Console.WriteLine(count) из списка actions, где count = 10.

Вопрос 4.

Что будет выведено на консоль в результате следующих операций:

int i = 1;
object obj = i;
++i;
Console.WriteLine(i);
Console.WriteLine(obj);
Console.WriteLine((short)obj);

Ответ:

Произойдет ошибка на этапе выполнения в последней строке. При упаковке в переменной типа int в переменную типа object распаковка может происходить только в этот же тип, а уже после этого можно производить другие приведения. (ссылка на пояснение на msdn)

Вопрос 5.

Что выведет на консоль следующий код:

var s1 = string.Format("{0}{1}", "abc", "cba");
var s2 = "abc" + "cba";
var s3 = "abccba";
 
Console.WriteLine(s1 == s2);
Console.WriteLine((object)s1==(object)s2);
Console.WriteLine(s2==s3);
Console.WriteLine((object)s2==(object)s3);

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

  1. true, false, true, true
  2. true, true, true, true
  3. true, false, true, false
  4. true, false, false, false

Ответ:

Ухххх...а с этим вопросом можно сломать мозг, здесь затрагивается тема интернирования строк. Ответом будет номер 1 (true false true true). Это происходит потому, что в C# есть пул интернирования в котором хранятся ссылки на строки, подробнее эта тема будет рассмотрена отдельно.

А вкратце можно ответить так. При запуске программы среда CLR находит все строковые литералы и добавляет их в пул интернирования до ее выполнения. При создании новых переменных с этими значениями в них помещается только ссылка из пула. Операция конкатенации воспринемается средой CLR в качестве целого строкового литерала (это будет наглядно если посмотреть код в программе ildasm), а вот операция String.Format создаёт строку уже в момент выполнения программы и переменная получит другую ссылку, хотя по значению они будут равны. Поэтому при сравнении ссылок на s1 и s2 программа нам выдает false, а в этот же момент s2 и s3 true.

Итог:

В этой статье были рассмотрены только пять вопросов, но дальше-больше, ждите обновлений, а также пишите в комментариях варианты своих вопросов и.

Источники:

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

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