Можете ли вы использовать переменные в их собственной строке инициализации (tryGetOrElse в словаре)? - PullRequest
4 голосов
/ 21 июля 2011

Рассмотрим следующий код:

private Dictionary<RobotSettings, Trader> createTradersFor(IEnumerable<RobotSettings> settings)
{
    var traderSet = new Dictionary<Tuple<IGateway, IBroker>, Trader>();

    return settings.ToDictionary(s => s, s =>
    {
        var key = Tuple.Create(s.gateway, s.broker);

        Trader trader = traderSet.TryGetValue(key, out trader)
            ? trader
            : traderSet[key] = new Trader(s.gateway, s.broker);
        return trader;
    });
}

Я говорю конкретно об инициализации переменной трейдера в замыкании, которое использует себя в той же строке, в которой оно создается.

В последнее время я часто использую этот шаблон для работы со словарями, потому что я действительно не люблю неинициализированные переменные :) и хотел бы знать, будет ли это гарантированно компилироваться в будущем.

Ответы [ 3 ]

5 голосов
/ 21 июля 2011

Кроме того, что это выглядит очень странно, в этом нет ничего плохого - технически.
Сначала будет выполнено объявление trader, поэтому существует объект Trader без назначенного значения. Во-вторых, часть TryGetValue оценивается и возвращает либо true, либо false. Если он вернул true, теперь будет присвоен присвоенный trader. Если он возвращает false, новый трейдер создается и добавляется в словарь с помощью операции присваивания. Результатом операции присваивания является значение объекта, который был присвоен. Это новый трейдер. В-третьих, результат троичного оператора будет возвращен и присвоен trader.

Маловероятно, что это изменится в будущем, потому что изменение порядка оценки такого утверждения является очень серьезным изменением.

UPDATE:
Потому что это выглядит очень странно, я бы не стал его использовать. Я бы решил эту проблему, создав метод расширения для IDictionary<TKey, TValue> с именем GetOrAdd.
Это может выглядеть так:

public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict,
                                            TKey key, Func<TKey, TValue> creator)
{
    TValue value;
    if(!dict.TryGetValue(key, out value))
    {
        value = creator(key);
        dict.Add(key, value);
    }
    return value;
}

Вы бы назвали это так:

var trader = traderSet.GetOrAdd(key, k => new Trader(s.gateway, s.broker));

Это намного чище и даже короче, чем ваш странный подход.

Кстати: вы можете использовать ConcurrentDictionary<TKey, TValue> вместо этого. Этот класс уже имеет метод GetOrAdd и имеет преимущество в том, что он безопасен для потоков.

2 голосов
/ 21 июля 2011

Гарантия - сильное слово, но очень, очень, очень маловероятно, что она прекратит компиляцию в будущем - языковые команды стремятся поддерживать обратную совместимость, и если не произойдет огромный сдвиг парадигмы (то есть с VB6 на первый VB.NET), этот код должен продолжать нормально работать.

Я действительно считаю, что это хороший трюк, но мне потребовалось некоторое время, чтобы увидеть, что TryGetValue использовался в троичной операции (и у других людей была такая же проблема)Таким образом, вы можете сохранить одну строку кода здесь (переместить объявление трейдера на одну строку вверх), но может потребоваться дополнительная плата за обслуживание, которую вы заплатите позже (или тому, кто унаследует этот код), поэтому, возможно, вам следует пересмотреть разделение декларации...

Trader trader;
trader = traderSet.TryGetValue(key, out trader) ?
...
1 голос
/ 21 июля 2011
Trader trader = traderSet.TryGetValue(key, out trader)
    ? trader
    : traderSet[key] = new Trader(s.gateway, s.broker);

против

Trader trader;
if (!traderSet.TryGetValue(key, out trader)) {
    trader = traderSet[key] = new Trader(s.gateway, s.broker);
}

Я тоже не вижу ничего плохого в том, что вы делаете, хотя я не уверен, что это лучше, чем простая альтернатива, которая не требуетбудущие программисты выяснят, что делает код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...