ИМХО, вместо того, чтобы вкручивать собеседникам мозги насчет заумной семантики event’ов и делегатов или же спрашивать когда написание new IEntity()
легально, можно задать вопрос попроще – например “приведите пример необычного поведения или использования языка C#”. Вот несколько примеров, которые пришли в голову в качестве возможных ответов.
Операторы | и & против || и &&
Обе пары операторов могут быть применены к переменным типа bool. Единственная разница – одиночные операторы не сокращают количество вычислений если результат очевиден:
bool b = false && a(); // a() not called
bool c = false & a(); // a() called
Передача null в качестве исключения
Как ни странно, передача null в качестве исключения приводит к выбросу NullReferenceException
:
try {
throw null;
} catch (NullReferenceException) {
// will be caught!
}
Странная стрелка при перечислении
Можно неплохо озадачить коллег если вместо типичного for при отсчете использовать while
с необычной стрелкой. Синтаксис абсолютно легален! Кстати – помнится тут на Хабре это кого-то смутило…
public IEnumerable<Foo> Create(int count)
{
while (count --> 0)
yield return new Foo();
}
Полезный оператор ??
Можно воспользоваться оператором ?? для ленивой инициализации объектов:
private IList<Person> people;
public IList<Person> People {
get {
return people ??
(people = new List<Person>());
}
}
Также можно использовать оператор для проверки цепочек значений:
int err(int? alpha, int? beta)
{
return alpha ?? beta ?? -1;
}
Хотя имхо для ленивости подходит и АОР. Или Boo :)
Методы расширения
Методы расширения можно применять не только к конкретным типам но и к интерфейсам. Это позволяет добавлять классам поведение не вклиниваясь в их иерархию наследования:
public static void T DeepCopy<T>
(this ICopyable<T> self)
where T : class, new()
{
⋮
}
}
В принципе расширения на интерфейсы это “поведенческие примеси” (behavior mixing). В отличии от F#, свойства примешивать нельзя, хотя если уж очень захотелось (warning: not recommended), можно написать что-нть вот такое:
static class ExtensionMethods
{
private static Dictionary<WeakReference, object> tags =
new Dictionary<WeakReference, object>();
public static void SetTag(this object obj, object tagValue)
{
Cleanup();
var found = tags.Select(o => o.Key)
.Where(k => ReferenceEquals(obj, k.Target));
if (found.Count() > 0)
{
tags[found.Single()] = tagValue;
}
else
tags[new WeakReference(obj)] = tagValue;
}
public static T GetTag<T>(this object obj)
{
return (T)tags.Where(o => ReferenceEquals(o.Key.Target, obj)).First().Value;
}
private static void Cleanup()
{
var toRemove = tags.Select(o => o.Key).Where(p => !p.IsAlive);
foreach (var tr in toRemove)
tags.Remove(tr);
}
}
Лямбды вместо строк
Иногда нужно держать в строковом литерале имя переменной. Вместо того чтобы использовать литерал напрямую, можно передать делегат на функцию и вытащить строку при разборе выражения. (Это делает код более безопасным и упрощает рефакторинг.)
public static string PropertyName<T>
(this object obj, Expression<Func<T>> p)
{
return (p.Body as MemberExpression).Member.Name;
}
Использовать эту конструкцию можно так:
lblName.DataBindings.Add(
PropertyName(() => Name), this, null);
Аналогичные манипуляции возможны и с методами. Да и вообще с чем угодно :)
Возврат значения из try-finally
Можно смело делать воврат из try-finally блока. При этом, компилятор закэширует возвращаемое значение, и оно будет возвращено только после исполнения finally-блока.
try {
a();
return x; // cached
}
finally {
b(); // called before x is returned
}
Креативное использование Dispose()
Интерфейс IDisposable можно использовать для создания блоков, вход и выход в которые нужно перехватить. Например, менять курсор на время выполнения операции:
class WaitCursor : IDisposable {
private Cursor oldCursor;
public WaitCursor(Cursor oldCursor) {
this.oldCursor = oldCursor;
SetCursor(Cursor.Wait);
}
public void Dispose() {
SetCursor(oldCursor);
}
}
Использовать этот конструкт можно так:
using (new WaitCursor(this.Cursor))
{
// long operation
}
Хотя для таких конструктов можно и АОР использовать.
Делегат-заглушка
Дабы не проверять событие на null, можно при декларировании подписать на событие делегат-заглушку:
public event MyClickHandler Click = delegate {};
В заключение
То, что работодатели напрягают собеседников всякими бредовыми задачками типа “единичный случай” это безусловно очень хорошо :) потому что нам становится легче с ними конкурировать за классных разработчиков.
Критика welcome!