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

Fan*_*ano 12 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}
};
Run Code Online (Sandbox Code Playgroud)

但是,在某些情况下,List<string>变量可能是空列表。如果是这种情况,我不想将{"hobbies", listHobbies}键添加到生成的字典中。

有没有办法在字典的集合初始值设定项中实现这个检查?还是我坚持使用 if 语句来检查列表的大小并在声明后立即删除密钥?

编辑:本质上,我想知道 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.
};
Run Code Online (Sandbox Code Playgroud)

我目前可以通过做这样的事情来实现这一点:

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);
Run Code Online (Sandbox Code Playgroud)

但这是非常丑陋的。

编辑 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);
Run Code Online (Sandbox Code Playgroud)

Dai*_*Dai 10

你可以

C# 的集合初始化器只是任何名为 的方法Add(包括扩展方法)的语法糖。无论每个项目之间使用什么 C# 语法,{}都将转换为Add方法参数。

所以这两个语句几乎是相同的(如果在初始化程序完成之前抛出异常,则集合对象生命周期略有不同,但这不值得担心)

// 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) }
};

Run Code Online (Sandbox Code Playgroud)

(这也意味着,如果您认为对象初始化器在某种程度上比重复调用更有效.Add(),或者它们甚至会导致constexpr集合的 C++ 风格编译时预初始化......那么不幸的是您错了:集合初始化器总是至少 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 );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

// 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 }
};
Run Code Online (Sandbox Code Playgroud)

更好的是:您可以使其通用(并且您可能无论如何都不应该使用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 );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是它在 LinqPad 中运行的屏幕截图证明:

在此输入图像描述


但为什么要停在那里呢?您不仅限于参数:例如,Boolean condition您可以使用一些粗糙的逻辑来检查 的状态,或者使用 a来确定每个项目在运行时的条件。this dictFunc<Boolean>

但有一个小警告:您的Add扩展方法的参数不能与原始方法完全Add匹配,否则您的扩展方法将不会被选择,因为扩展方法总是具有较低的优先级。您也不能通过使用显式参数名称(集合初始化程序没有命名参数)来强制选择扩展方法,但您可以使用自定义类型将虚拟参数用作第三个参数struct(因此它实际上为零成本)。

例如,这是我用来用Exception.Data失败请求中的信息简洁地填充 的字典的扩展HttpClientresponseBody如果值为 ,则该扩展会跳过null

internal readonly 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 );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用:

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) }
        }
    };
}
Run Code Online (Sandbox Code Playgroud)


jom*_*k1e 1

这有什么问题吗?

var dict = new Dictionary<string, dynamic>() 
{
    {"firstName", firstName},
    {"lastName", lastName}
};
if (listHobbies.Any())
    dict.Add("hobbies", listHobbies);
Run Code Online (Sandbox Code Playgroud)

  • 没有什么特别的原因,我一直在学习函数式编程原理并尝试将它们应用到我的其他项目中,在 C# 中这样做可能不是最佳实践,这种语言在创建时并没有考虑到这些事情(我想象)。尽管如此,我只是好奇这是否可能。 (2认同)