Как стать автором
Обновить

Комментарии 92

Что так удивляет в конструкции throw null?

Просто следует помнить, что throw null из C# это совсем не то же самое, что throw NULL из С++. Первое является некорректным выражением и приводит к исключению, второе эквивалентно throw 0.
ИМХО Руки отрывать надо за throw null.
Надо.
НЛО прилетело и опубликовало эту надпись здесь
Вроде такой же подход применяется в реализации Html.ActionLink<T>() в ASP.NET MVC Futures
Кстате — через «И».
А так спасибо — интересно.
Интересно, спасибо.

Правда ИМХО это к собеседованиям как-то мало применимо… Трюки — они трюки и есть.
while (count --> 0)

Это гораздо лучше в целях читаемости кода писать как while (count-- > 0). И, кстати, Визуал Студия сама предлагает такой вариант по умолчанию.
Здесь весь прикол именно в стрелочке =)
Сам конструкт может запутать. Если кто-то например не знает что while (x—>0) действительно пройдет по всем значениям от x-1 до 0 включительно. Кстате, легально и следующее: while (0<—x). Но это конечно менее гламурно.
while (count --> 0)

вообще, это дикримент(--) и сравнение(>), по отдельности, а не «странная стрелочка».

ЗЫ меня поначалу смутило :)
жуткий, неочевидный perl-style :)
Да, Капитан!
кстати, декремент )
IDisposable — креативненько. Не очень представляю реальные сценарии, но забавно.

Насчет throw null — сработает, но стэк поломает, правильно? Это же будет ошибка для throw, которому передали null вместо инстанса исключения. Т.е. это просто вызов одной поломки вместо другой — по сути вы замаскируете ошибку.

Лямбды — а можно пример из реального сценария, как это может использоваться?

Очепятка
«Медоды расширения можно применять не только ...»
IDisposable я использовал в продашкн под формз до того как познакомился с PostSharp.

throw null — да, должно стек поломать. Не для продакшна — только для быстрого выброса исключения при тестировании.

Лямбды — используется в Fluent NHibernate, например.
про IDisposable тут пришло правильное замечание — а если кто использует метод без Using :)
ну в таком случае по-хорошему будет вызывать метод Dispose() ручками, что повлечет за собой тот же эффект
… Тот сам себе злобный буратино — в .net conventions сказано: «если класс реализует IDisposable, необходимо его использовать через using (или вызвать IDispose) руками».
ээх, ваши слова да 90% программистам в уши…

Если сущность допускает неправильный вызов — она будет неправильно вызвана :)
>>если сущность допускает неправильный вызов — она будет неправильно вызвана
Это уже переделка законов Мерфи какая-то :)
Вот в Воо можно расширить компилятор чтобы по рукам било. А в шарпе придется терперь…
А в шарпе можно бить по рукам злобных буратин, чтобы они учились и совершенствовались — таким образом можно избежать большего числа проблем, чем вводя ограничения компилятора каждый раз. :D
Смотря в какой стране идет разработка ;) ИМХО в некоторых локейшнах как раз использование StyleCop, FxCop, maintainability metrics и прочих злостных вещей как раз самое то. Только вот не думаю что они там на Воо пишут ;)
Муахаха, для нашей команды самый главный FxCop, NDepend, simian и Gendarme — это я. Меня уже все ненавидят :)
Много джуниоров в проекте?
Формально — один (я :D)
У меня такая же ерунда была, когда джуниором был )
Всем надоел со стилями программирования и именования, борьбой с «оригинальными» подходами в алгоритмах и архитектуре.
А счас перестали, что ли?

Формально я джуниор, фактически — техлидер/архитектор на 2х проектах, так что тут обратная аналогия — вы в бытность junior всех доставали, я — на подходах к senior developer :)
Это архитипическая проблема нашей индустрии наверное — когда джуниоры намного полезней на проекте чем интермедиаты, сениоры и даже лида. Только в «сервисах» хз как это разруливать ;)
Ммм… на текущем месте работы, с которого я ухожу, сложилось хрупкое равновесие — программисты делают вид, что работают, а начальство делает вид, что им верит.
При это заказчик доволен, потому что пилит бюджет, и ему пофиг на качество, ему нужно отчеты о работе: баг исправили, два внесли, но это никого не волнует, главное — процесс.

Говорить о том, что кто-то заинтересован в качестве — я думаю, излишне; я уже писал, что ведущий программист проекта не умеет мержить ветки в репозитории, и отказывается это делать. Unit-тесты прожили ровно 3 месяца, пока я ходил и ругался. Перестал орать — перестали писать и даже запускать. О continuos integration никто и слышать не хочет, просил Фаулера прочитать — куммулятивный ответ таков: «я не буду заниматься этим в свободное время, а на работе мне на это не выделят ресурс».

Junior я тут, потому что на 3 субпроекта — 2 архитектора, 3 тестера + 1 тестлид, 1 менеджер, 1 ведущий программист и 1 mid. Ясно, что тут нужен кто-то, кто будет писать код, а не совещаться по 4 часа в переговорке. А вот тот, кто пишет код — и есть code-monkey, т.е. jun. Доходит до смешного — после коммитов «команды» в репу я вынимаю код и переписываю его :)

*Рамазывает слезы и разговаривает с портретом МакКоннела на обложке* — Но мы же им покажем, правда, Стиви?
Ситуация знакомая. Естественно что талантливому разработчику там не место. Но с другой стороны, есть очень мало фирм которые в принципе могу саппортить очень прогрессивных начинающих специалистов. Взять ту же заказную разработку — как человека продать подороже и дать зарплату побольше если у него много навыков и талантов, но небольшой послужной список? Заказчик ведь не будет вникать в эти тонкости.

При этом я не хочу сказать что сервисные разработчики чем-то хуже продуктовых. Никак нет! Просто продуктовым легче, потому что профессионализм классного спеца отражается на качестве продукта и тем самым на продажах. В «сервисах», увы, цепочка подлиннее.
… и чем ближе, тем больше, муаххаха!
О IDisposable — таким же образом реализован FileStream.
С IDisposable это использование средств языка не по назначению, соответственно плохой стиль. Нужно писать понятный код, а не рыть ямы на каждом шагу.
Как раз тут не соглашусь, хотя в других случаях это возможно и так. IDisposable используют многие, и это считается вполне нормальным явлением.
А я очень часто использую такую конструкцию в UI.
Код
private void BeginLoading()
{
    IsLoading = true;
}

private void EndLoading()
{
    IsLoading = false;
}

protected IDisposable Loading()
{
    return new LoadingHelper(this);
}

private class LoadingHelper : IDisposable
{
    private readonly BaseViewModel _viewModel;
    private readonly bool _wasLoading;

    public LoadingHelper(BaseViewModel viewModel)
    {
        _viewModel = viewModel;
        _wasLoading = _viewModel.IsLoading;
        if (!_wasLoading)
        {
            _viewModel.BeginLoading();
        }
    }

    public void Dispose()
    {
        if (!_wasLoading)
        {
            _viewModel.EndLoading();
        }
    }
}


Можно хоть во вложенных методах вызывать. IsLoading станет false только после выполнения самого внешнего using.
По мне так это тоже относительно относящиеся к собеседовнию вопросы. Что значит необычное поведение или использование языка?
Я сам постоянно использую конструкции описанные в «Креативное использование Dispose()» для смены настроение Graphics, но не подумал бы что это что-то неординарное.
Что значит полезность оператора _??_? Либо о нем знают либо нет. Что тут необычного?
Лямбды не использую — пишу на 2.0.
В любом случае, каждый приведенный шаблон только с натяжкой можно назвать чем-то необычным. На собеседовании Вы вряд ли соберетесь выдавить из памяти хоть пару чего-то похожего. Потому что по памяти сложно вспоминать то, что и словами то описать сложно.

По мне так самые лучшие вопросы на собеседовании те, которые вообще имеют малое отношение к используемой технологии, если конечно должность не требует ее отменного знания. Например, почему колодцы на улицах круглые, сколько домов в городе. Изходя из логики рассуждение «испытуемого» можно сложить представление подходит он или нет.

А именно по технологии, то вопросов о базовых знаний вполне хватит. Например, сортировать на требуемом языке символы во входящей строке. Вообще, все что может потребоваться от соискателя можно узнать в неформальной обстановке, неформальными вопросами — делел/не делал, где использовал.
> bool b = false && a(); // a() not called
> bool c = false & a(); // a() called
странный пункт. правда я не гуру c#, но по аналогии с другими языками думаю, что приведенные две строки вообще разные по сути. соответсвенно нет ничего удивительного, что для побитового сравнения «a() called», потому что конструкция false & a() возвращает число, а не bool.
Это специфика C#. Этот вопрос фигурирует на тесте Brainbench. Биты тут как бы не причем.
т.е. в C# 2 & 3 != 2?
Я имел ввиду для булевых значений.
хорошо, спрошу по-другому. если a() возвращает int то, строки:
bool c = false & a();
и
bool c = (bool)( (int)false & a() );

скомпилируются в один и тот же код или нет?
а в случае, если a() возвращает bool?

неужели в c# оператор & компилируется по-разному в зависимости от операндов?
боюсь (int)false не прокатит ни в какой версии
У нас, вообще-то, не С++ а C#, и если написать такой вот тест:

static bool a() { return true;  }
static bool b() { return false; }
static void Main(string[] args)
{
  bool c = a() && b();
  bool d = a() & b();
}

То получите вот такой вот IL:

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 2
    .locals init (
        [0] bool c,
        [1] bool d)
    L_0000: nop 
    L_0001: call bool ConsoleSandbox.Program::a()
    L_0006: brfalse.s L_000f
    L_0008: call bool ConsoleSandbox.Program::b()
    L_000d: br.s L_0010
    L_000f: ldc.i4.0 
    L_0010: stloc.0 
    L_0011: call bool ConsoleSandbox.Program::a()
    L_0016: call bool ConsoleSandbox.Program::b()
    L_001b: and 
    L_001c: stloc.1 
    L_001d: ret 
}

>У нас, вообще-то, не С++ а C#
да, я IL и имел ввиду.

а с приведением типов я перемудрил, видимо.
И не только C#. В PHP то же самое
быть может я немного не понимаю о чем речь :)
в PHP:

function a()
{
return 2;
}

$res = true | a(); // $res — int, значение 3
в шарпе операции типа | & допускаются только для одинаковых типов
// прокатит
int a = 1 | 3;
bool b = true | false;

// не прокактит
int c = 1 | false;

p.s. а в плюсах всё прокатывало

Простите, а это фишки какой версии .NET`а?
В основном 3й версии шарпа.
дык больше половины для 2
Варнинг: ленивая инициализация через ?? в вашем примере потоконебезопасна :) Имхо не стоит сразу учить людей плохому :)
Поставил lock(this) и стала потокобезопасной, проблем то.

Не нравится lock — поставил Mutex.
вообще Mutex это конечно масштабно :) имхо lock'у тут самое место
Так я и не говорю, что проблема :)
Однако lock — тоже «вещь в себе», хорошая тема для собеседований.
Товарищ Джувал Лови учит, что lock(this) можно только для private/internal типов, т.к. иначе нет никаких гарантий, что я например не захочу использовать ваш объект как sync root.
ну тогда lock(myobject)
жалко что вы в питере:)
ребята, кстати а как у вас там с alt.net движением?
В Мск отсутствует. Так что spbalt.net вне конкуренции. Пока :)
думаете нам стоит приехать, чтобы началось?
Ага. И не уезжать :))
Тогда ожидаем очень привлекательных и веских предложений
Мы сейчас и Инету вытянем на должный уровень.
Так приезжайте в гости.
Делегат-заглушка это здорово.

только ведь этож наверное надо проводить на уровне code convention, иначе один так а дугой по старинке… и привет NullReferenceException
Это все равно не лучшая практика. Как учит товарищ Джувал Лови, вызов событий нужно оформлять отдельным методом с атрибутом [MethodImpl(MethodImplOptions.NoInlining)], чтобы исключить нежелательные оптимизации JIT компиляции и недопустить race condition.
занятно, но для собеседования также сомнительно. Большинство пунктов показывает знаком человек с «последними» фишками или нет. Над стрелочкой где-то минуту протупил, думал когда этот оператор могли добавить и как его пропустил =)
А вот в Java
throw null именно кидает null

А посему кусок из реального кода
try {
… some code…
} catch (Throwable t) {
try {
t.printStackTrace();
throw t;
} catch (Exception ex) {
ex.printStackTrace();
throw t;
}
}

PS Действительно, люди задают на собеседованиях очень странные и глупые вопросы. И абсолютно не задают то, что действительно важно.
> А вот в Java throw null именно кидает null

Неправда.
JLS 14.18:
If evaluation of the Expression completes normally, producing a null value, then an instance V' of class NullPointerException is created and thrown instead of null.
Любопытно. Да, я ошибся. Но мне удавалось как-то ловить эксепшены == null. Видимо, там все хитрее. Спасибо, что указали, надо будет еще разок ту проблему порыть поглубже, как там получился такой эффект любопытный.
э, я один не увидел здесь ни одного трюка?
bool b = false && a(); // a() not called
bool c = false & a(); // a() called
В любом нормальном языке так и будет работать ибо для логического и не нужно второе значение, если первое 0.

try {
throw null;
} catch (NullReferenceException) {
// will be caught!
}
имхо логично, а что ему еще делать то, а для понимания я бы не стал так делать, а кинул стандартным методом.

public IEnumerable Create(int count)
{
while (count --> 0)
yield return new Foo();
}
имхо закосы под brainfuck не есть гуд, как и написано в описании, это смутит того кто увидит код? а оно тебе надо, это голые понты.

С последним как раз все нормально, вполне читаемый код. А стрелочка, ну это шутка такая, для поддержания тонуса :) Для тех кому не нравится, есть Visual Basic.
В командной разработке за такие шутки руки надо отрубать, т.к. сильно мешается, даже такая мелочь сбивает с толку, а это потеря времени, потеря эффективности труда.
Неужели все настолько плохо, что мысленно переставить один пробел — это большая проблема?
когда приходится просматривать тысячи строк кода и один пробел временами сложно домыслить.
Я бы добавил в пункт «Делегат-заглушка», что это поле надо обозначать как несереализуемое, либо клас никогда не должен сериализоваться.

Про «Странная стрелка при перечислении» при первом автоформатировании документа его разорвёт, и вообще это из рода извращений, а их можно очень много привести, например:

protected void TryReadValueType<_>(int index, out @_? @struct)
where _: struct
{
object @object = _command.Parameters[index];

@struct = @object == DBNull.Value? default(@_?): (@_?)@object ?? default(@_?);
}

Приведу от себя несколько полезных вещей:
1) using CounterKeyValue = System.Collections.Generic.KeyValuePair<string, decimal>;

2) Статические поле
public abstract class MyClasswhere T: MyClass{
private static bool _someField = some_value;

}
public class MyClass2: MyClass{
}
public class MyClass3: MyClass{
}

MyClass2 и MyClass3 будут иметь разные статические поля _someField

3) Конструкторы для для хендлеров
Func<int, int, Func> handlerConstructor =
(dx, dy) => () => {
return dx + dy;
};

event1 += handlerConstructor (10, -1);
event2 += handlerConstructor (10, -3);

и др. отвлекаться лень :-)
В примере про статическое поле съелость часть кода хабрапарсером
public abstract class MyClass<T>
where T: MyClass<T>
{
private static bool _someField = some_value;

}
public class MyClass2: MyClass<MyClass2>
{
}
public class MyClass3: MyClass<MyClass3>
{
}

А вообще позже напишу больше трюков какие использую, как появится время.
Спасибо! Мне добавить их в статью? С ссылкой на Вас, естественно.
Как хотите =) Ваше право ;-)
Насчет делегата-заглушки на событиях — интересно, подобное описание никак не помешает сборщику мусора удалить объект, у которого события таким вот образом «проинициализированы»?
Интересно, а если в случае try-finally написать, например, x=b(), что вернет функция? В смысле, копируется ли значение x там, где стоит return или в момент настоящего возврата? Подозреваю первое. Или нет?
Я тоже подозреваю первое. Суть в том, что насколько я помню кэшируется то значение которое должно было бы быть возвращено.
В пункте лямбды вместо строк, есть ошибка. PropertyName описан как экстеншн
public static string PropertyName(this object obj, Expression<Func> p).
а исспользуется как просто метод lblName.DataBindings.Add(PropertyName(() => Name), this, null);
правильно было бы просто написать
public static string PropertyName(Expression<Func> p).

Еще одно небольшое замечание, логичнее писать
lblName.DataBindings.Add(PropertyName(() => lblName. Name), this, null);
т.к. по сути вы используется имя Name из текущего контекста, а не имя свойства объекта lblName для которого добавляете биндинг.

Ага, насколько я помню, для класса нужно написать this.PropertyName(...). Я специально вырезал все излишества чтобы показать только саму суть — а именно то, что передавая выражение, можно выдирать из него текстовую сущность. Я сам не фанат WinForms data binding, если честно.
в данном контексте this.PropertyName тоже станно смотриться, т.к. вы хотите получить имя свойства объекта lblName.
Для понимания лучше lblName.PropertyName(), хотя это приведет к дублированию кода lblName.PropertyName(() => lblName. Name). С другой стороны это позволит в код экстеншена добать проверку, совпадает ли тип объекта для которого вызываем экстеншен, с типом у которого берем имя свойства.
var memberExpression = p.Body as MemberExpression;
Debug.Assert(memberExpression.Expression.Type == obj.GetType());
Согласен, есть в ваших примерах необычное, но большинство «трюков» проходится на первом курсе в универе и за не знание оных можно схватить незачет )
Например я точно уже видел вот эти фишки:
Операторы | и & против || и &&
Полезный оператор ??
Методы расширения
Возврат значения из try-finally
Делегат-заглушка
Но все равно большое спасибо
интересно где вы видели чтобы на 1 курсе проходили расширения?
вопрос не понял :)
расширения, они же exstensions. Это все к тому что обычно 1 курс это всякие сортировки и тд, и вытекающие отсюда синие экраны Pascal))
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации