Набор интересных головоломок по C#. Часть 2
C# Puzzle №1 (уровень: начинающий)
Вы хотели бы увидеть коллекцию, которая чудесным образом создает элемент, как только вы попросите его? Ну, вот она. Опрашиваем коллекцию на существование ключа и, вам будет сообщено, что элемент существует в коллекции.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
namespace ConsoleApplication
{
class Program
{
static void Main( string[] args )
{
NameValueCollection Collection = new NameValueCollection();
Console.WriteLine(
"NameValueCollection магическим образом создает элементы? " +
Collection["foo"] != null ? " Да, он существует" : "Нет, его нету."
);
}
}
}
Сможете найти "ошибку"?
C# Puzzle №2 (уровень: средний)
Следующий фрагмент кода обычно используется в С/С++ для обмена значений двух целочисленных значений без помощи каких-либо дополнительных переменных:
int x, y;
x ^= y ^= x ^= y;
Это не работает в C#. Можете ли вы объяснить, почему?
C# Puzzle №3 (уровень: начинающий)
Могут ли две статические переменные, которые обращаются к друг другу, привести к бесконечной рекурсии?
public class A
{
public static int a = B.b + 1;
}
public class B
{
public static int b = A.a + 1;
}
public class MainClass
{
public static void Main()
{
Console.WriteLine( "A.a={0}, B.b={1}", A.a, B.b );
}
}
Можете ли вы объяснить, каков будет результат (если таковые имеются) приведенного выше кода без компиляции?
C# Puzzle №4 (уровень: продвинутый)
В тех случаях когда метод ожидает параметр и возвращает что - то, Вы можете написать:
parameter => expression
вместо:
ReturnType Foo( ParameterType parameter )
{
expression;
}
Вопрос:как Вы предоставите лямбда-выражение для делегата, который не ожидает параметров?
delegate int FooDelegate();
static void Foo( FooDelegate Delegate )
{
Console.WriteLine( Delegate() );
}
static void TheProblem()
{
Foo( * );
}
Ваша цель состоит в том, чтобы заполнить * выше лямбда выражением эквивалентное следующему:
int ConvertThisToLambdaExpression()
{
return 5;
}
C# Puzzle №5 (уровень: продвинутый)
Можете предсказать результат выполнения кода без компиляции?
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
list.FindAll( i => { Console.WriteLine( i ); return i < 5; } );
C# Puzzle №6 (уровень: продвинутый)
Если Вы правильно решили 5 задачу, то с этой у вас тоже не должно быть проблем:
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
list.Where( i => { Console.WriteLine( i ); return i < 5; } );
C# Puzzle №7 (уровень: продвинутый)
Сколько раз будет напечатано X?
List<int> list = new List<int>() { 1, 2, 3 };
list.GroupBy ( i => { Console.Write( "X" ); return i; } );
list.ToLookup( i => { Console.Write( "X" ); return i; } );
В этой задаче необходимо подумать о различии двух методов
C# Puzzle №8 (уровень: начинающий)
В приведенном ниже коде есть два варианта: либо компилятор выберет метод из базового класса, имеющие такую же сигнатуру или метод фактической класса, для которого необходимо выполнить преобразование параметра..
class A
{
public void Foo( int n )
{
Console.WriteLine( "A::Foo" );
}
}
class B : A
{
/* A::Foo и B::Foo никак не связаны */
public void Foo( double n )
{
Console.WriteLine( "B::Foo" );
}
}
static void Main( string[] args )
{
B b = new B();
/* Какой Foo будет выбран? */
b.Foo( 5 );
}
Какой метод будет вызван? Можете объяснить поведение компилятора?
C# Puzzle №9 (уровень: начинающий)
Рассмотрим объявление делегата, который принимает функцию и возвращает другую функцию:
delegate Func<int, int> FuncConversionDelegate( Func<int, int> Func );
Ваша цель написать лямбда выражение, которое будет изменять знак возвращаемого значения:
FuncConversionDelegate Negation =
/*
сюда вставте лямбда выражение которое
принимает функцию f и возвращает
функцию g, такую что g(x) = -f(x)
*/;
Чтобы проверить возьмите функцию Identity:
static Func<int, int> Identity = x => x;
и примените Neation:
Negation( Identity )( 5 )
Должно вернуться -5.
C# Puzzle №10 (уровень: начинающий)
Возможно ли, чтобы класс А имел доступ к приватным членам класса В? Конечно нет. Это нарушение инкапсуляции - наиболее распространенный ответ. Тем не менее существует по крайней мере один случай, где это на самом деле происходит и даже является очень важным элементом языка.
Поэтому вопрос: C#, в каких обстоятельствах класс А имеет прямой доступ к закрытым членам другого класса B?
ОТВЕТЫ
C# Puzzle №1
Оператор + имеет больший приоритет чем ?:следовательно сначала происходит конкатенация со строкой, а затем проверка на null.
C# Puzzle №2
Перед тем, как выражение вычисляется, значения аргументов помещается в стек, поэтому присваивание обновляет только переменные, но не аргументы выражения. Выражение осуществляется следующим образом:
- Поместим в стек x, y, x и y. Стек выглядит так:
y0
x0
y0
x0x это x0, y -y0
2. Применяем XOR двух верхних значений и сохраняем в x. Стек:
x0 ^ y0
y0
x0x это x0 ^ y0.
y это y0.
3. Применяем XOR двух верхних значений и сохраняем в y. Стек:
x0 ^ y0 ^ y0 = x0
x0x is x0 ^ y0
y is x0
4. Применяем XOR двух верхних значений и сохраняем в x. Стек:
x0 ^ x0 = 0
x is 0
y is x0
C# Puzzle №3
Это не приведет к бесконечной рекурсии, потому что инициализации будет происходить только один раз. Выходные данные должны быть:
A.a=2, B.b=1
Порядок вызова:
- Вызывается статический конструктор класса А.
- Перед тем, как поле А.а будет проинициализировано, будет вызван статический конструктор B.
- Инициализация поля В.b. Поле А.а еще не инициализировано и имеет значение по умолчанию. Происходит инкрементирование на 1 и сохранение в B.b. Поле теперь равно 1.
- Инициализация поля А.а. B.b уже инициализирован и имеет значение 1. Происходит инкрементирование на 1 и сохранение в А.а. Поле теперь равно 2.
C# Puzzle №4
Ответ прост: () => 5
C# Puzzle №5
В документации говориться "Этот метод выполняет линейный поиск; поэтому он является операцией O(n), где n является значением свойства Count.". Делегат, передаваемый в качестве аргумента вызывается для каждого элемента в списке. Программа выведет:
1
2
3
4
5
6
7
8
9
10
C# Puzzle №6
Список будет пустым. Запрос, представленный данным методом, не выполняется до тех пор, пока не будет произведено перечисление объекта путем непосредственного вызова его метода GetEnumerator или с помощью оператора foreach.
C# Puzzle №7
Отличие заключается в том, что ToLookup выполняется немедленно. Выедено будет XXX, вызов GroupBy ничего не выведет.
C# Puzzle №8
Будет вызван B:Foo(double n). Потому что компилятор дает приоритет методам из того же класса.
C# Puzzle №9
FuncConversionDelegate Negation = func => i => -func(i);
C# Puzzle №10
Все вложенные классы имеют доступ к приватным членам "верхнего" класса.
Источник: http://netpl.blogspot.com
7
Подписаться на:
Комментарии к сообщению (Atom)
20 сентября 2010 г. в 10:18
А где ответы? :)
20 сентября 2010 г. в 12:49
В следующем посте напишу...
20 сентября 2010 г. в 17:40
>>Поэтому вопрос: C#, в каких обстоятельствах класс А имеет прямой доступ к закрытым членам другого класса B?
В случае явной реализации интерфейса.
20 сентября 2010 г. в 18:41
Это набор головоломок очень похож на статью - как не надо писать код.
20 сентября 2010 г. в 18:45
Правда не все - пару "пазлов" использовать можно :)
21 сентября 2010 г. в 14:15
Здравствуйте. Считаю себя не начинающим, но задумался над первым заданием. Озадачился (вроде, как ошибок и нет), посидел, почесал репу, решил на практике проверить. Вот мой код:
NameValueCollection collection = new NameValueCollection();
Console.WriteLine(string.Format("Blah-blah-blah {0}", collection["foo"] != null ? "Yes" : " No"));
И вот что мне выдало:
Blah-blah-blah No
Так что тут у меня сейчас двойственное чувство. "То ли я идиот, то ли лыжи не едут." О_о
21 сентября 2010 г. в 16:42
Нет... просто надо было переписать как есть, а не пользоваться форматом :)
Оператор "?:", в данном коде, выполняется не на коллекцию, а на весь кусок кода Т.е. он проверяет не равна ли null строка ("NameValueCollection магическим образом создает элементы? " + Collection["foo"]). Просто оператор "+" имеет приоритет выше оператора "?:"