c# - 在集合初始值设定项中使用条件

标签 c# dictionary

我有一些值想放入字典中:

// 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 中工作的截图证明:

enter image description here


但为什么要停在那里呢?您不限于 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/

相关文章:

c# - 来自 App.xaml.cs 的 wp7 刷新页面

c# - 每个请求的依赖注入(inject)容器

java - 如何在 Google map 上显示地点自动完成结果

javascript - 在javascript中将某种格式的字符串转换为JSON

c++ - 如何在 C++ 中反向迭代 map ?

c# - 在 C# 中使用文档属性

c# - 查看模型和依赖注入(inject)

c# - 使用字典作为其他字典中的键

java - 在 Java 中将流转换为字符串

c# - 为 C# 制作 C++ DLL