我有一些值想放入字典中:
// Pretend these values are retrieved from a database or something
string firstName = "John";
string lastName = "Smith";
List<string> listHobbies = new List<string> () {"hockey","soccer"};
var dict = new Dictionary<string, dynamic>()
{
{"firstName", firstName},
{"lastName", lastName},
{"hobbies", listHobbies}
};
但是,在某些情况下,List<string>
变量可能是一个空列表。如果是这种情况,我不想添加 {"hobbies", listHobbies}
结果字典的键。
有没有办法在字典的集合初始化器中实现这个检查?还是我坚持使用 if 语句来检查列表的大小并在声明后立即删除 key ?
编辑: 本质上,我想知道 C# 是否允许这样的事情:
var dict = new Dictionary<string, dynamic>()
{
{"firstName", firstName},
{"lastName", lastName},
{"hobbies", listHobbies} unless (listHobbies.Count == 0) // don't add the "hobbies" key if the list is empty.
};
我目前可以通过做这样的事情来实现:
var dict = new Dictionary<string, dynamic>()
{
{"firstName", firstName},
{"lastName", lastName},
{"hobbies", listHobbies}
};
}.Concat(listHobbies != 0 ? new Dictionary<string, dynamic>() { { "hobbies", listHobbies } } : new Dictionary<string, dynamic>()).ToDictionary(k => k.Key, v => v.Value);
但这非常丑陋。
编辑 2: 我找到了一个可以接受的解决方案,如果其他人可以提供更好看的替代方案,请随时提出建议:
var dict = new Dictionary<string, dynamic>()
{
{"firstName", firstName},
{"lastName", lastName},
{"hobbies", !listHobbies.Any() ? listHobbies : null}
}
.Where(entry => entry.Value != null)
.ToDictionary(k => k.Key, v => v.Value);
最佳答案
你可以
C# 的集合初始化器只是任何名为 Add
的方法的语法糖,包括扩展方法。无论您在每个项目的 {}
之间使用什么 C# 语法将被翻译成Add
方法参数。
所以这两个语句几乎相同(如果在初始化器完成之前抛出异常,collection-object-lifetime 会略有不同,但这不值得担心):
// Manually:
Dictionary<String,Foo> dict = new Dictionary<String,Foo>();
dict.Add( "foo1", new Foo(1) );
dict.Add( "foo2", new Foo(2) );
dict.Add( "foo3", new Foo(3) );
dict.Add( "foo4", new Foo(4) );
// Collection-initializer:
Dictionary<String,Foo> dict = new Dictionary<String,Foo>()
{
{ "foo1", new Foo(1) },
{ "foo2", new Foo(2) },
{ "foo3", new Foo(3) },
{ "foo4", new Foo(4) }
};
(这也意味着,如果您认为对象初始化器在某种程度上比重复调用 .Add()
更有效率,或者它们甚至会导致 C++ constexpr
风格集合的编译时预初始化……不幸的是,你错了:集合初始化器总是至少 O(n)
运行时——如果你需要更有效的初始化,考虑使用数组支持数据结构,以便您可以使用预初始化数组)。
因此,如果你想要一个条件初始化器,首先为你的集合类型(在本例中为 Dictionary<String,dynamic>
)定义扩展方法,并添加一个额外的 Boolean condition
。参数:
public static class MyDictionaryExtensions
{
public static void Add( this Dictionary<String,dynamic> dict, String key, dynamic value, Boolean condition )
{
if( condition )
{
dict.Add( key, value );
}
}
}
然后你可以像这样使用它:
// Pretend these values are retrieved from a database or something
string firstName = "John";
string lastName = "Smith";
List<string> listHobbies = new List<string> () {"hockey","soccer"};
var dict = new Dictionary<string, dynamic>()
{
{ "firstName", firstName },
{ "lastName" , lastName },
{ "hobbies" , listHobbies, listHobbies.Count > 0 }
};
更好的是:你可以让它通用(而且你可能不应该使用 dynamic
无论如何):
public static class MyDictionaryExtensions
{
public static void Add<TKey,TValue>( this Dictionary<TKey,TValue> dict, TKey key, TValue value, Boolean condition )
where TKey : notnull
{
if( condition )
{
dict.Add( key, value );
}
}
}
这是它在 LinqPad 中工作的截图证明:
但为什么要停在那里呢?您不限于 Boolean condition
参数:你可以有一些粗糙的逻辑来检查 this dict
的状态,例如,或使用 Func<Boolean>
确定每个项目在运行时的条件。
但有一个小警告:您的 Add
扩展方法的参数不能完全匹配原始 Add
方法,否则你的扩展方法不会被选择,因为扩展方法总是有较低的优先级。您也不能使用显式参数名称强制选择您的扩展方法(集合初始化器没有命名参数),但您可以使用自定义参数将虚拟参数用作第三个参数struct
类型(因此它实际上具有零成本)。
例如,这是我用来简洁地填充 Exception.Data
的扩展的字典,其中包含失败的信息 HttpClient
请求,它会跳过 responseBody
如果值为 null
:
internal struct OnlyIfBodyIsNotNull
{
}
internal static class DictionaryExtensions
{
public static void Add( this IDictionary exDataDict, String key, String? body, OnlyIfBodyIsNotNull _ )
{
if( body != null )
{
exDataDict.Add( key, body );
}
}
}
这样使用:
public static InvalidOperationException CreateException( String message, HttpResponseHeaders responseHeaders, HttpContentHeaders contentHeaders, String? responseBody = null )
{
return new InvalidOperationException( message )
{
Data =
{
{ "Response.Headers" , responseHeaders.ToString() },
{ "Response.ContentHeaders", contentHeaders .ToString() },
{ "Response.Body" , responseBody , default(OnlyIfBodyIsNotNull) }
}
};
}
关于c# - 在集合初始值设定项中使用条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23003237/