Оба способа одинаковы в действии. Первый способ использует методы для чтения из коллекции, а второй способ использует индексатор для достижения того же. Фактически индексатор внутренне вызывает TryGetValue()
.
При вызове MyClass myClass = concurrentDictionary[key]
(или concurrentDictionary[key].MyClassOperation()
) словарь внутренне выполняет метод получения свойства индексатора:
public TValue this[TKey key]
{
get
{
if (!TryGetValue(key, out TValue value))
{
throw new KeyNotFoundException();
}
return value;
}
set
{
if (key == null) throw new ArgumentNullException("key");
TryAddInternal(key, value, true, true, out TValue dummy);
}
}
Внутренний код ConcurrentDictionary
показывает, что
concurrentDictionary.TryGetValue(key, out value)
и
var value = concurrentDictionary[key]
одинаковы, за исключением того, что индексатор выдает KeyNotFoundException
, если клавиша не существует.
Из точки потребления Просмотр первой версии с использованием TryGetValue
позволяет писать более читаемый код:
// Only get value if key exists
if (concurrentDictionary.TryGetValue(key, out MyClass value))
{
value.Operation();
}
против
// Only get value if key exists to avoid an exception
if (concurrentDictionary.Contains(key))
{
MyClass myClass = concurrentDictionary[key];
myClass.Operation();
}
Говоря о читабельности, ваш код может быть упрощен следующим образом:
private async Task DoJob(string myKey)
{
if (dict.TryGetValue(myKey, out MyClass myClass))
{
var value = myClass.SomeValueToRead;
myClass.Prop1 = 10;
await myClass.DoSomeAnotherJob();
}
}
- Поскольку async / await был разработан для асинхронного выполнения операции в потоке пользовательского интерфейса,
await myClass.DoSomeAnotherJob()
не будет блокироваться. - не будет
TryGetValue
или this[]
блокировать другие потоки - оба варианта доступа выполняются с одинаковой скоростью, поскольку они используют одну и ту же реализацию
dict[myKey].Operation()
равно
MyClass myclass = dict.[myKey];
myClass.Operation();
.
То же самое, когда
GetMyClass().Operation()
равно
MyClass myClass = GetMyClass();
myClass.Operation();
- ваше восприятие неверно. Ничто не называется "внутри" словаря. Как видно из фрагмента внутреннего кода, dict [key] возвращает значение.
Замечания
ConcurrentDictionary
- это способ обеспечить поточно-ориентированный доступ к коллекции. Например, это означает, что поток, который обращается к коллекции, всегда будет иметь доступ к определенному состоянию коллекции. Но учтите, что сами элементы не являются поточно-ориентированными, поскольку они хранятся в поточно-ориентированной коллекции:
Учтите, что следующий метод выполняется двумя потоками одновременно . Оба потока совместно используют один и тот же ConcurrentDictionary
, содержащий, следовательно, общие объекты.
// Thread 1
private async Task DoJob(string myKey)
{
if (dict.TryGetValue(myKey, out MyClass myClass))
{
var value = myClass.SomeValueToRead;
myClass.Prop1 = 10;
await myClass.DoSomeLongRunningJob();
// The result is now '100' and not '20' because myClass.Prop1 is not thread-safe. The second thread was allowed to change the value while this thread was reading it
int result = 2 * myClass.Prop1;
}
}
// Thread 2
private async Task DoJob(string myKey)
{
if (dict.TryGetValue(myKey, out MyClass myClass))
{
var value = myClass.SomeValueToRead;
myClass.Prop1 = 50;
await myClass.DoSomeLongRunningJob();
int result = 2 * myClass.Prop1; // '100'
}
}
Также ConcurrentDictionary.TryUpdate(key, newValue, comparisonValue)
аналогичен следующему коду:
if (dict.Contains(key))
{
var value = dict[key];
if (value == comparisonValue)
{
dict[key] = newValue;
}
}
Пример : Допустим, словарь содержит элемент цифры c в ключе "Amount" со значением 50. Поток 1 хочет изменить это значение, только если поток 2 не изменил его за это время. Значение потока 2 является более важным (имеет приоритет). Теперь вы можете использовать метод TryUpdate
, чтобы применить это правило:
if (dict.TryGetValue("Amount", out int oldIntValue))
{
// Change value according to rule
if (oldIntValue > 0)
{
// Calculate a new value
int newIntValue = oldIintValue *= 2;
// Replace the old value inside the dictionary ONLY if thread 2 hasn't change it already
dict.TryUpdate("Amount", newIntValue, oldIntValue);
}
else // Change value without rule
{
dict["Amount"] = 1;
}
}