Когда выдается KeyNotFoundException, как узнать, какой ключ не найден?
System.Collections.Generic.Dictionary
бросает KeyNotFoundException
, но я не вижу, какой ключ предположительно отсутствует. Как мне это определить?
8 ответов
Нет никакого способа отличить это от исключения. Вы должны реализовать собственное решение для этого.
Пользовательское исключение:
class WellknownKeyNotFoundException : KeyNotFoundException
{
public WellknownKeyNotFoundException(object key, string message)
: this(key, message, null) { }
public WellknownKeyNotFoundException(object key, string message, Exception innerException)
: base(message, innerException)
{
this.Key = key;
}
public object Key { get; private set; }
}
Удобный метод расширения:
public TValue GetValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
{
try
{
return dic[key];
}
catch (KeyNotFoundException ex)
{
throw new WellknownKeyNotFoundException((object)key, ex.InnerException);
}
}
Использование:
var foo = new Foo();
var bar = new Bar();
IDictionary<Foo, Bar> dic = new Dictinary<Foo, Bar>
{
{ foo, bar }
};
try
{
dic.GetValue(foo);
}
catch (WellknownKeyNotFoundException ex)
{
var key = (Foo)ex.Key;
Assert.AreEqual(foo, key); // should be
}
Если вы можете настроить реализацию, в которой объявлен словарь, вы можете легко заменить System.Collections.Generic.Dictionary на пользовательский тип, выдав более приятное KeyNotFoundException. Хотя это похоже на ответ Абатищева, мне не нравится метод расширения, который он ввел, поскольку это означает, что у нас есть два разных способа добиться абсолютно одинаковой вещи. Этого следует избегать, если это возможно. Я решил проблему, используя вместо этого "NiceDictionary", который можно использовать точно так же, как исходный Dictinary, используемый в качестве базового класса. Реализация почти тривиальна:
/// <summary>
/// This is a nice variant of the KeyNotFoundException. The original version
/// is very mean, because it refuses to tell us which key was responsible
/// for raising the exception.
/// </summary>
public class NiceKeyNotFoundException<TKey> : KeyNotFoundException
{
public TKey Key { get; private set; }
public NiceKeyNotFoundException(TKey key, string message)
: base(message, null)
{
this.Key = key;
}
public NiceKeyNotFoundException(TKey key, string message, Exception innerException)
: base(message, innerException)
{
this.Key = key;
}
}
/// <summary>
/// This is a very nice dictionary, because it throws a NiceKeyNotFoundException that
/// tells us the key that was not found. Thank you, nice dictionary!
/// </summary>
public class NiceDictionary<TKey, TVal> : Dictionary<TKey, TVal>
{
public new TVal this[TKey key]
{
get
{
try
{
return base[key];
}
catch (KeyNotFoundException knfe)
{
throw new NiceKeyNotFoundException<TKey>(key, knfe.Message, knfe.InnerException);
}
}
set
{
try
{
base[key] = value;
}
catch (KeyNotFoundException knfe)
{
throw new NiceKeyNotFoundException<TKey>(key, knfe.Message, knfe.InnerException);
}
}
}
}
Как уже говорилось, вы можете использовать его точно так же, как вы использовали бы оригинальный словарь. Он волшебным образом работает из-за переопределенного оператора массива ([]).
Вы не можете просто, глядя на исключение. Вам придется взломать отладчик при возникновении исключения (Debug -> Exceptions... в Visual Studio) и посмотреть, к какому ключу был получен доступ. В качестве альтернативы вы можете перехватить исключение в коде и распечатать его (например, на консоли).
Используйте отладчик (при необходимости проверьте ThrowOnCatch в Debug->Exceptions) и посмотрите
Я использовал этот метод расширения
internal static class KeyNotFoundExceptionExtensions
{
public static string GetKeyValue(this KeyNotFoundException ex)
{
var match = Regex.Match(ex.Message, @"'(.*?)'");
if (match.Success)
return match.Groups[1].Value;
throw ex;
}
}
System.Collections.Generic.KeyNotFoundException выброшено
Если вы используете DotNet Core с Xamarin Studio и получаете эту ошибку, вы можете проверить, существует ли ключ при следующем условии:
if (Application.Current.Properties.ContainsKey("userCredentials")) {
//now process...
}
Вы могли бы подумать, что они могут добавить запрошенный ключ в exception.data
это то, что я сделал
public static void SetGlobals()
{
string currentKey = "None";
try
{
currentKey = "CurrentEnvironment";
Globals.current_environment = Settings[currentKey];
currentKey = "apiUrl";
Globals.api_url = Settings[currentKey];
currentKey = "whatever";
Globals.whatever= Settings[currentKey];
if (AppDomain.CurrentDomain.GetData(".devEnvironment") as bool? == true)
Globals.api_url = "http://localhost:59164/api";
}
catch(KeyNotFoundException)
{
DBClass.logEvent("Error", "AppSettings", "Missing Setting: " + currentKey);
}
}