Как я могу сделать этот код TryGetValue словаря более читабельным?
Я хотел бы проверить, был ли идентификатор еще не известен или, если он известен, изменилось ли связанное значение. В настоящее время я использую код, подобный этому, но это трудно понять тем, кто не знаком с шаблоном. Можете ли вы придумать способ сделать его более читабельным, оставляя его коротким в LOC?
string id;
string actual;
string stored;
if (!someDictionary.TryGetValue (id, out stored) || stored != actual) {
// id not known yet or associated value changed.
}
10 ответов
Это выглядит хорошо для меня... читается так же легко, как и любые другие 2 условия if. Единственное, что я мог бы изменить, это перевернуть отрицания для раннего выхода:
if (someDictionary.TryGetValue(id, out stored) && stored == actual) {
return;
}
// store new value
Я не вижу в этом никакой путаницы, никогда не думал об этом как об особенно неприятной идиоме и смиренно предполагаю, что те разработчики C#, которые смущены этим, привыкнут к этому. Это обычное дело, лаконично, и дает столько LOC, сколько заслуживает. Превращение в 10 строк кода делает его слишком важным.
Если я использовал это часто, метод расширения с именем что-то вроде ContainsEqualValue
было бы уместно - но я бы использовал тот же код в методе расширения, что и у вас.
Вы можете написать метод расширения с хорошим именем:
public static class Utility
{
public static bool ValueChangedOrUnknown(this Dictionary<string, string> dictionary, string id, string actual)
{
string stored = null;
return (!dictionary.TryGetValue(id, out actual) || stored != actual);
}
}
так что позже вы можете использовать
string id;
string actual;
if (someDictionary.ValueChangedOrUnknown(id, actual) {
// id not known yet or associated value changed.
}
Поэтому я, скорее всего, разбил бы его и дал бы ему значимые имена. Это больше для чтения, но вам не нужно много говорить в комментариях:
bool isKnown = someDictionary.TryGetValue (id, out stored);
// can only change when it is known
bool valueChanged = isKnown && stored != actual;
// quite self-explanatory, isn't it?
if (!isKnown || valueChanged)
{
}
Обернуть каждую часть || в свой собственный метод или свойство, чем вы можете написать это так
if ( IdIsNew() || IdChanged())
Двойственность.
if (!(someDictionary.TryGetValue (id, out stored) && stored == actual)) ...
Не уверен, что это более читабельно, хотя... но это полезно знать.
Я бы предпочел новый метод:
public bool ShouldSetValue(Dictionary someDictionary, object id,object actualValue)
{
string stored;
if (someDictionary.TryGetValue (id, out stored))
{
if (stored != actualValue)
return true;
}
else
{
return true;
}
}
тогда в существующем методе я бы просто:
if (ShouldSetValue(someDictionary,id,actual))
{
someDictionary[id]=actual;
}
Если вы имеете в виду, что вам приходится делать это неоднократно, и это долго и безобразно, абстрагируйте логику от другого класса и используйте метод расширения.
public static class DictionaryExtensions
{
public static DictionaryChecker<TKey,TValue> contains<TKey,TValue>(this IDictionary<TKey,TValue> dictionary, TValue value)
{
return new DictionaryChecker<TKey,TValue>(value, dictionary);
}
}
public class DictionaryChecker<TKey,TValue>
{
TValue value;
IDictionary<TKey,TValue> dictionary;
internal DictionaryChecker(TValue value, IDictionary<TKey, TValue> dictionary)
{
this.value = value;
this.dictionary = dictionary;
}
public bool For(TKey key)
{
TValue result;
return dictionary.TryGetValue(key, out result) && result.Equals(value);
}
}
Теперь замените ваш код на:
if(!someDictionary.contains(actual).For(id)){
// id not known yet or associated value changed.
}
public T GetValue(int id, object actual)
{
object stored;
if (someDictionary.TryGetValue (id, out stored) || stored == actual)
return stored;
return new object();
}
Метод расширения будет гладким:
public static class DictionaryExtensions
{
public static bool ShouldAddValue<TKey, TValue>(this Dictionary<TKey, TValue> someDictionary, TKey id, TValue actual)
{
TValue stored;
return (!someDictionary.TryGetValue(id, out stored) || !stored.Equals(actual));
}
}
Использование:
someDictionary.ShouldAddValue("foo", "bar")
Хотя я признаю, что шаблон "try" необходим, мне не нравятся реализации, для которых требуется параметр "out". Казалось бы, гораздо полезнее иметь функции, похожие на TryGetValue:
- TryGetDictValue(словарь, ключ) возвращает ноль, если ключ отсутствует в словаре
- TryGetDictValue(словарь, ключ, defaultValue) возвращает defaultValue, если ключ отсутствует в словаре
- TryGetDictValue(dictionary, key, valueReturningDelegate) вызывает предоставленный делегат, если ключ отсутствует в словаре, и возвращает его результат
В каждом случае возвращаемый тип результата будет типом данных словаря.
Жаль, что нет способа проникнуть в машину времени и сделать такие вещи методами словаря. С другой стороны, их можно реализовать как статические функции, используя словарь в качестве первого параметра.