Хочу представить вашему вниманию комические купле каверзные вопросы по 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(); }
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(); }
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(); }
Здесь все чуть более очевидно по сравнению с прошлым примером.
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 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;
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; }
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 раз. Причем была взята ссылка на переменную 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 выполнен"); } }
Метод 2
Блок if выполнен не будет, так как SomeMethod1 возвращает false. Но, так как используется логический оператор &, то будет проверено и второе условие – SomeMethod2. Если бы использовался более привычный оператор &&, то проверено было бы только значение первого метода.
9 Еще один простой (даже можно сказать бородатый) вопрос. Что будет результатом выполнения следующего кода?
double e = 2.718281828459045; object o = e; // box int ee = (int)o;
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());
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;
На всякий случай коротенько о различиях между 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)
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 Что такое дословные/точные строки (verbatim strings)?
Verbatim strings — это строки, которые начинаются с символа @ и в которых не обрабатываются escape-последовательности.
Как их правильно называть на русском — дословные или точные это уже отдельный вопрос.
ссылка на оригинал статьи https://habrahabr.ru/post/275789/
Добавить комментарий