.net: Добавление словарного элемента - проверить, существует ли он или разрешить исключение? - PullRequest
15 голосов
/ 12 сентября 2009

Я добавляю элементы в StringDictionary и, возможно, появится дубликат ключа. Это, конечно, вызовет исключение.

Если вероятность дубликатов очень мала (т. Е. Это случается редко), лучше ли мне использовать блок Try Catch и оставить его необработанным, или я всегда должен делать проверку .ContainsKey перед добавлением каждой записи?

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

Мысли

Редактировать

Я использовал рефлектор в общем словаре и обнаружил следующее для ContainsKey и TryGetValue, так как оба были упомянуты ниже.

public bool TryGetValue(TKey key, out TValue value)
{
    int index = this.FindEntry(key);
    if (index >= 0)
    {
        value = this.entries[index].value;
        return true;
    }
    value = default(TValue);
    return false;
}

И

public bool ContainsKey(TKey key)
{
    return (this.FindEntry(key) >= 0);
}

Я что-то упустил или TryGetValue выполняет больше работы, чем ContainsKey?


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

Ответы [ 9 ]

16 голосов
/ 12 сентября 2009

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

// c# sample:
myDictionary[key] = value;

В качестве примечания: я бы, по возможности, использовал Dictionary<string, string> вместо StringDictionary. Если ничего другого, это даст вам доступ к еще нескольким методам расширения Linq.

7 голосов
/ 12 сентября 2009

Я бы сделал проверку Содержит.

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

5 голосов
/ 12 сентября 2009

Если это вообще возможно, замените StringDictionary на Dictionary<string, string> и используйте TryGetValue. Это позволяет избежать как затрат на обработку исключений, так и двойного поиска.

4 голосов
/ 14 апреля 2013

Я сделал несколько сравнительных тестов по этому поводу. Но я должен повторить точку зрения Келси:

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

Это имеет смысл, потому что выигрыш в производительности, полученный за try-catch (если он вообще есть), тривиален, но «улов» может быть более наказывающим. Вот тест:

public static void Benchmark(Action method, int iterations = 10000)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < iterations; i++)
        method();

    sw.Stop();
    MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());
}

public static string GetRandomAlphaNumeric()
{
    return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}

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

Нет дубликатов:

Benchmark(() =>
{
    // approach 1
    var key = GetRandomAlphaNumeric();
    if (!dict.ContainsKey(key))
        dict.Add(item, 0);

    // approach 2
    try
    {
        dict.Add(GetRandomAlphaNumeric(), 0);
    }
    catch (ArgumentException)
    {

    }
}, 100000);  

50% дубликатов:

for (int i = 0; i < 50000; i++)
{
    dict.Add(GetRandomAlphaNumeric(), 0);  
}

var lst = new List<string>();
for (int i = 0; i < 50000; i++)
{
    lst.Add(GetRandomAlphaNumeric());
}
lst.AddRange(dict.Keys);
Benchmark(() =>
{
    foreach (var key in lst)
    {
        // approach 1
        if (!dict.ContainsKey(key))
            dict.Add(key, 0);

        // approach 2
        try
        {
            dict.Add(key, 0);
        }
        catch (ArgumentException)
        {

        }
    }
}, 1);  

100% дубликатов

var key = GetRandomAlphaNumeric();
dict.Add(key, 0);
Benchmark(() =>
{
    // approach 1
    if (!dict.ContainsKey(key))
        dict.Add(item, 0);

    // approach 2
    try
    {
        dict.Add(key, 0);
    }
    catch (ArgumentException)
    {

    }
}, 100000);

Результаты

Нет дубликатов

подход 1: отладка -> 630 мс - 680 мс; расцепление -> 620 мс - 640 мс

подход 2: отладка -> 640 мс - 690 мс; расцепление -> 640 мс - 670 мс

50% дубликатов

подход 1: отладка -> 26 мс - 39 мс; расцепление -> 25 мс - 33 мс

подход 2: отладка -> 1340 мс; выпуск -> 1260 мс

100% дубликатов

подход 1: отладка -> 7 мс; выпуск -> 7 мс

подход 2: отладка -> 2600 мс; расцепление -> 2400 мс

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

2 голосов
/ 12 сентября 2009

Если это не очень большой словарь или критический внутренний цикл кода, вы, вероятно, не увидите разницы.

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

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

1 голос
/ 16 декабря 2011

Вы можете расширить словарь с помощью метода AddorUpdate ():

http://williablog.net/williablog/post/2011/08/30/Generic-AddOrUpdate-Extension-for-IDictionary.aspx

0 голосов
/ 01 июня 2019

Эта проблема намного проще на данный момент. Просто используйте метод TryAdd. Он решает все проблемы, которые у вас есть наиболее изящным способом

0 голосов
/ 05 апреля 2017

Есть 2 способа посмотреть на это ...

Performance

Если вы смотрите на это с точки зрения производительности, вы должны учитывать:

  • как дорого вычислить хэш типа для ключа и
  • как дорого создавать и генерировать исключения

Я не могу придумать вычисления хэша, которое было бы дороже, чем создание исключения. Помните, что когда генерируются исключения, их нужно маршалировать через взаимодействие с Win32 API, создавать, сканировать всю трассировку стека, останавливать и обрабатывать любой обнаруженный блок перехвата. Бросок исключений в CLR по-прежнему рассматривается как HRESULT, обрабатываемый Win32 API под крышками. Из блога Криса Брамма :

Конечно, мы не можем говорить об управляемых исключениях без предварительного рассмотрения структурированной обработки исключений Windows (SEH). И нам также нужно взглянуть на модель исключений C ++. Это потому, что как управляемые исключения, так и исключения C ++ реализованы поверх базового механизма SEH, а также потому, что управляемые исключения должны взаимодействовать как с SEH, так и с C ++. исключения.

Производительность Заключение: избегайте исключений


Лучшая практика

.NET Framework Design Guidelines - хороший набор правил для подражания (за редким исключением - каламбур). Обновление рекомендаций по проектированию: исключение . В руководящих принципах упоминается что-то под названием «Try / Doer», в этом случае рекомендуется избегать исключений:

Рассмотрите шаблон Tester-Doer для участников, которые могут генерировать исключения в общих сценариях, чтобы избежать проблем производительности, связанных с исключениями.

Исключения также никогда не следует использовать в качестве механизма управления потоком - снова написано в Руководстве по проектированию CLR.

Вывод из передового опыта: избегайте исключений

0 голосов
/ 12 сентября 2009

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

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