Вопросы на собеседованиях по C# и .Net. На позицию junior/middle. Часть 1.
Совсем скоро настанет момент, когда нужно будет устраиваться на другую работу, а, соответственно, начнутся и собеседования. Для того чтобы не облажаться получить должность, желательно подготовиться. Поэтому появилась идея создать серию статей, в которых будут собраны различные вопросы, которые могут Вам попасться, и дать на них ответы. Материалы брались из различных источников, список будет в конце. Предлагайте в комментариях свои варианты вопросов, они так же будут добавлены в общий список. Итак, начнем!
!!НАШ БЛОГ ПЕРЕЕХАЛ!!
Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда.
Наш новый сайт maddevelop.ru
А данную статью вы можете найти по ссылке ниже:
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
с Модификаторамиstatic
,abstract
,private
, или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());
Варианты ответов:
- true, true
- true, false
- false, true
- 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(); }
Что будет выведено на консоль? Варианты ответов:
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
- 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
- Код сгенерирует исключение
- 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);
Варианты ответов:
- true, false, true, true
- true, true, true, true
- true, false, true, false
- true, false, false, false
Ответ:
Ухххх...а с этим вопросом можно сломать мозг, здесь затрагивается тема интернирования строк. Ответом будет номер 1 (true false true true). Это происходит потому, что в C# есть пул интернирования в котором хранятся ссылки на строки, подробнее эта тема будет рассмотрена отдельно.
А вкратце можно ответить так. При запуске программы среда CLR находит все строковые литералы и добавляет их в пул интернирования до ее выполнения. При создании новых переменных с этими значениями в них помещается только ссылка из пула. Операция конкатенации воспринемается средой CLR в качестве целого строкового литерала (это будет наглядно если посмотреть код в программе ildasm), а вот операция String.Format создаёт строку уже в момент выполнения программы и переменная получит другую ссылку, хотя по значению они будут равны. Поэтому при сравнении ссылок на s1 и s2 программа нам выдает false, а в этот же момент s2 и s3 true.
Итог:
В этой статье были рассмотрены только пять вопросов, но дальше-больше, ждите обновлений, а также пишите в комментариях варианты своих вопросов и.
Источники: