我有一个Configuration
类,其中包含许多设置。一个简单的例子:
class Configuration
{
public string Name { get; set; }
public string Url { get; set; }
public string Password { get; set; }
//There are about 20 of these
}
我想为调用者提供从字符串字典填充此对象的功能,如下所示:
static Configuration CreateFromDictionary(Dictionary<string, string> dict)
{
try
{
return new Configuration
{
Name = dict["Name"],
Url = dict["Url"],
Password = dict["Password"]
}
}
catch(KeyNotFoundException exception)
{
throw new ArgumentException("Unable to construct a Configuration from the information given.");
}
}
这很好,除了它是一个全有或全无的转换。如果调用方提供的字典大部分都很好,但是其中一个条目的拼写错误,则转换将失败,并出现异常。
我希望能够提供更好的异常消息,告知调用者哪个密钥未找到。似乎很重要。但是我无法从KeyNotFoundException
检索该信息。
我可以编写代码来一次解析字典的每一行并分别检查每个键,但这似乎是很痛苦的。是否有任何方法可以判断是否从异常信息中找不到哪个密钥,或者不用ContainsKey
或TryGetValue
一次只搜索一行密钥?
答案 0 :(得分:1)
不幸的是,Dictionary<,>
中的索引器抛出的异常'KeyNotFoundException'没有在错误消息中提供'key'值。
以下是可用于获取详细异常的通用扩展方法。同样在catch代码中的代码中,您正在吞没异常。而是将其作为InnerException放入,以便正确记录。
static Configuration CreateFromDictionary(Dictionary<string, string> dict)
{
try
{
return new Configuration
{
Name = dict.GetValue("Name"),
Url = dict.GetValue("Url"),
Password = dict.GetValue("Password")
}
}
catch (KeyNotFoundException ex)
{
throw new ArgumentException("Unable to construct a Configuration from the information given.", ex);
}
}
public static class ExtensionsUtil
{
public static Tvalue GetValue<Tvalue, TKey>(this Dictionary<TKey, Tvalue> dict, TKey key)
{
Tvalue val = default(Tvalue);
if (dict.TryGetValue(key, out val))
{
return val;
}
else
{
throw new KeyNotFoundException($"'{key}' not found in the collection.");
}
}
}
答案 1 :(得分:1)
您可以将键及其映射存储在字典中,并在映射值之前验证输入:
gcloud projects create tf-admin-001 --name tf-admin --organization xxxx --set-as-default
答案 2 :(得分:0)
使用字典的“ TryGetValue”方法,并丢弃捕获处理程序。如果找不到密钥,则TryGetValue的结果将为false,否则将为true。如果返回true,则存在一个“出”参数。代替捕获处理程序,您只需跟踪不存在的键(再次由TryGetValue的false的返回值指示)。
简单示例:
string name;
Configuration config = new Configuration();
if (dict.TryGetValue("Name", out name))
{
config.Name = name;
}
else
{
throw new ArgumentException("'Name' was not present.");
}
编辑: 根据对该问题的说明,我将答案修改为:不,不可能从异常中获取该信息。
答案 3 :(得分:0)
我过去解决此问题的方法是包装真正的字典:
public class MyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
public MyDictionary(IDictionary<TKey, TValue> realDictionary)
{
_dictionary = realDictionary;
}
...
public TValue this[TKey key]
{
get
{
try
{
return _dictionary[key];
}
catch (KeyNotFoundException e)
{
throw new KeyNotFoundException($"Key {key} is not in the dictionary", e);
}
}
...
}
...
}
不幸的是,内置异常根本不提供信息。
答案 4 :(得分:0)
到目前为止,我的解决方案是使用扩展方法:
public static T ParseFor<T>(this IDictionary<string, string> source, string key)
{
string val;
if (!source.TryGetValue(key, out val)) throw new KeyNotFoundException(string.Format("An entry for '{0}' was not found in the dictionary", key));
try
{
return (T)Convert.ChangeType(val, typeof(T));
}
catch
{
throw new FormatException(string.Format("The value for '{0}' ('{1}') was not in a correct format to be converted to a {2}", key, val, typeof(T).Name));
}
}
并像这样使用它:
return new Configuration
{
Name = dict.ParseFor<string>("Name"),
Url = dict.ParseFor<string>("Url"),
Password = dict.PasreFor<string>("Password")
}
扩展方法将抛出一个有用的异常,其中包含密钥名称。还具有支持类型转换的好处,例如如果配置项之一是bool
,它将尝试对其进行转换。
答案 5 :(得分:0)
您可以编写一个构建器,该构建器将找到您的Configuration
类型中存在的'string'类型的属性,然后遍历这些属性,尝试从给定的字典中获取每个属性。该代码甚至可以是通用的:
public T Build<T>(Dictionary<string, string> source)
where T : new()
{
var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty;
var properties = typeof(T).GetProperties(flags)
.Where(p => p.PropertyType == typeof(string));
var missingKeys = properties.Select(p => p.Name).Except(source.Keys);
if (missingKeys.Any())
throw new FooException($"These keys not found: {String.Join(", ", missingKeys)}");
var obj = new T();
foreach (var p in properties)
p.SetValue(obj, source[p.Name]);
return obj;
}
用法:
// throws FooException if any key is missing
var config = Something.Build<Configuration>(dict);
但是通常,配置不包含字符串。
Url
无效的uri怎么办? Name
为空怎么办?Password
在字典中是null
怎么办? 在所有这些情况下,您的代码都会很高兴,并且您将在运行时遇到异常。如果您使用特定类型的设置string
,Uri
,int
,DateTime
等,情况会好得多。并尽快验证这些值。通常,有些设置是可选的-如果字典或您使用的任何来源缺少此设置,则不要抛出该错误。
public class Configuration
{
public string Name { get; set; }
public Uri Url { get; set; }
public int RequiredNumber { get; set; }
public int? NotRequiredNumber { get; set; }
}
然后,您可以创建一组扩展方法,这些方法尝试从字典中获取和解析值:
public static Uri GetUri(this Dictionary<string, string> source, string key)
{
if (!source.TryGetValue(key, out string value))
throw new ConfigurationException($"{key} not found");
if (!Uri.TryCreate(value, UriKind.Absolute, out Uri result))
throw new ConfigurationException($"{key} is not a valid uri: {value}");
return result;
}
创建配置看起来像
var config = new Configuration {
Name = dict.GetString("Name"),
Url = dict.GetUri("Uri"),
RequiredNumber = dict.GetInt("RequiredNumber"),
NotRequiredNumber = dict.GetNullableInt("NotRequiredNumber")
};
如果配置错误,您的代码将很快失败。创建配置后,您将非常安全。
答案 6 :(得分:0)
这是您可以做的。
static Configuration CreateConfiguration(Dictionary<string,string> dict)
{
try
{
return
new Configuration
{
FirstName = dict.getValue("FirstName"),
LastName = dict.getValue("LastName"),
ID = dict.getValue("ID")
};
}
catch (Exception ex)
{
throw ex;
}
}
public static class Extension
{
public static string getValue(this Dictionary<string, String> dict, string keyName)
{
string data = null;
var result = dict.TryGetValue(keyName, out data);
if (!result)
throw new KeyNotFoundException($"The given key '{keyName}' was not present in the dictionary. keyname is not found");
return data;
}
}