Почему .net не предоставляет нам ключ, когда он вызывает исключение KeyNotFound (и как я могу его получить?) - PullRequest
4 голосов
/ 20 мая 2009

Когда вы пытаетесь получить доступ к ключу, которого нет в Словаре (например), вот трассировка стека, которую вы получаете:

   at System.ThrowHelper.ThrowKeyNotFoundException()
     at System.Collections.Generic.Dictionary`2.get_Item(TKey key)   
       .... .... (my own code stack trace)

Как и многие другие, я регистрирую эти ошибки, когда они появляются, и пытаюсь выяснить, что произошло.

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

Либо я неправильно выглядел (класс KeyNotFoundException содержит элемент «Данные», который всегда пуст, а поле «Сообщение» не содержит значения ключа), либо он не был включен в рамки вообще.

Я не могу представить, чтобы никто из команды .net BCL думал, что это будет полезной функцией. Мне любопытно узнать, почему они не включили это. Есть ли веские причины не делать этого?

Как вы справляетесь с этими исключениями? Единственная альтернатива, о которой я могу подумать, - это использовать пользовательский метод расширения в словаре, который бы обернул вызов, перехватил исключение и перебросил его дополнительной информацией о ключе, но это не помогло бы для кода, которым я не владею, и это кажется сломанным, чтобы изменить такую ​​"базовую" функциональность.

Что вы думаете?


Мне известен этот вопрос , касающийся общего факта, что нельзя получить доступ к аргументам метода, вызвавшего исключение. Мой вопрос конкретно связан с KeyNotFoundException.


Редактировать: Мне известен шаблон TryGetValue, я делаю это все время, когда ожидаю, что моя коллекция может не содержать ключ. Но когда он должен содержать его, я не проверяю, потому что я предпочитаю, чтобы моя программа не работала с исключением, чтобы я знал, что что-то неожиданное произошло до (то есть в момент, когда ключ должен был быть вставлен)

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

Ответы [ 4 ]

4 голосов
/ 20 мая 2009

Почему вы полагаетесь на (медленные) исключения? Просто проверьте, существует ли ключ с ContainsKey или TryGetValue?

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

class ParameterizedKeyNotFoundException<T> : KeyNotFoundException {
    public T InvalidKey { get; private set; }

    public ParameterizedKeyNotFoundException(T InvalidKey) {
        this.InvalidKey = InvalidKey;
    }
}

static class Program {

    static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> Dict, TKey Key) {
        TValue res;

        if (Dict.TryGetValue(Key, out res))
            return res;

        throw new ParameterizedKeyNotFoundException<TKey>(Key);
    }

    static void Main(string[] args) {

        var x = new Dictionary<string, int>();

        x.Add("foo", 42);

        try {
            Console.WriteLine(x.Get("foo"));
            Console.WriteLine(x.Get("bar"));
        }
        catch (ParameterizedKeyNotFoundException<string> e) {
            Console.WriteLine("Invalid key: {0}", e.InvalidKey);
        }

        Console.ReadKey();
    }
}
1 голос
/ 20 мая 2009

Как указал Дарин, вы действительно должны учитывать это в своем коде, но если по какой-либо причине это невозможно, вы можете сделать следующее.

public object GetObjectFromDictionary(string key)
{
    try
    {
        return MyDictionary[key];
    }
    catch (KeyNotFoundException kex)
    {
        throw new WrappedException("Failed To Find Key: " + key, kex);
    }
}
1 голос
/ 20 мая 2009

Вместо этого можно использовать метод ContainsKey или TryGetValue, чтобы проверить, содержит ли словарь ключ.

0 голосов
/ 20 мая 2009

Я не могу осветить вас, почему ключ не включен в исключение. Как сказал Дарин, вы всегда должны использовать TryGetValue - это облегчает чтение, сопровождение кода, и вы не получите исключение KeyNotFoundException (и, несмотря на это, всегда используйте TryParse вместо Parse, для int, double, DateTime , что угодно, по той же причине).

Однако, как только вы их получили:

Как вы справляетесь с этими исключениями?

Поскольку вы знаете, где выбрасывается исключение, если код не является полностью сложным, это позволяет воспроизвести ошибку (включите «Разорвать на выброшенное исключение», или как там это называется в английской версии, в Debugging-> Диалог исключений для этого исключения прерывается в тот самый момент), и я обычно выясняю причину через несколько минут. Отладчик сообщает вам, что это за ключ в этом случае, и тогда ваша задача - выяснить, почему это ключ (или почему ключ отсутствует в словаре), даже если вы получили имя ключа в отчете об ошибке. Если у вас нет своего рода словаря богов, где есть абсолютно все, выяснить, в чем проблема, довольно просто.

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

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

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

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