C#属性初始化语法名称

3
Headers属性中使用的语法名称是什么? Headers被定义为public HttpRequestHeaders Headers {get;}。 让我感到困惑的是表达式的左侧不是setter。
我在c#的隐藏特性C#历史中没有找到它。
var tokenRequest = new HttpRequestMessage
{
    Method = HttpMethod.Post,
    RequestUri = new Uri("http://localhost"),
    Headers = {
        { HttpRequestHeader.Authorization.ToString(), "abc123" },
        { HttpRequestHeader.ContentType.ToString(), "application/x-www-form-urlencoded" }
    },
    Content = new FormUrlEncodedContent(new Dictionary<string, string> { ["grant_type"] = "client_credentials" })
};

为什么要是setter,这只是一个初始化列表设置Headers属性。 - pm100
4
您可能正在寻找此页面:使用集合只读属性初始化的对象初始值设定项 - UnholySheep
你是否感到困惑,readonly int foo = 5; 这样的代码是合法的,尽管它被声明为“只读”?这是同样的概念。 - Kirk Woll
@KirkWoll readonly int foo = 5; - 不,这是在类初始化(构造之前)期间分配的字段。对我来说设置字段是可以的。我不同意它是相同的,因为此处的属性没有setter。 - JJS
这只是一个初始化列表,设置了Headers属性。 这就是我要找的答案 ;) 我的大脑从未将其视为集合初始化语法处理的情况。 - JJS
显示剩余4条评论
2个回答

2
这是使用集合只读属性初始化的对象初始化器
引用块中说:“左侧表达式不是 setter 让我感到头疼。”
使用集合初始化器,即使没有定义set,也可以在构造函数期间设置属性的值。对于集合,上面链接的文档如下所述:
“集合初始化器允许您在初始化实现IEnumerable并具有Add作为实例方法或扩展方法的集合类型时指定一个或多个元素初始化程序。”
这可能非常奇怪(或很酷,取决于您的看法),因为您可以为几乎任何IEnumerable类型构建自己的Add()扩展方法,并使用它来做一些非常有趣的事情。
还要记住,当使用一个既是集合又是属性的属性时,您无需使用set来调用对象或集合中的方法或更改属性值。相反,您首先获取到对该集合的引用,然后使用由该嵌套属性提供的任何访问方式。
例如,假设您有一个名为tokenRequest的变量,它具有类型为HttpRequestHeadersHeaders属性。 HttpRequestHeaders类型又有一个Add()方法。要调用该方法,您首先获取对Headers属性的引用,然后在该引用上调用方法。不会使用或需要set,但您仍然成功更改了仅定义了get的属性。
集合初始化器利用此功能。

当使用对象初始化程序时,即使没有定义set,您也可以在构造函数期间设置值或属性。这个解释是不正确的。并没有调用Set。正如您链接的文档中“带有集合只读属性初始化的对象初始化程序”部分所解释的那样,在集合初始化程序中省略new会使用获取器来调用集合的Add方法,以添加大括号中的每个项。 - Nigel
@NigelBess 是的,我知道,我正在修复它。我重新阅读了最初的帖子,并意识到它是误导性的。现在稍微好一些了,但还有更多的编辑要进行。 - Joel Coehoorn
@JoelCoehoorn建议:由于本文标题为“使用集合只读属性初始化的对象初始化程序”,您是否介意将链接更改为该标题,并链接到该标题? https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#object-initializers-with-collection-read-only-property-initialization - JJS

1
我不确定是否有官方名称,但我看到它被称为集合初始化器的“鸭子类型”,由编译器隐式完成。编译器将查找类型实现的适当Add方法,以及类型必须实现IEnumerable接口。 HttpRequestMessage.Headers最终符合这两个条件,即使该属性只有getter,编译器也会将集合初始化器转换为在对象创建后连续的“Add”调用。

1
哇,那是一些可怕的设计。这意味着等号左边容易出现“NullReferenceException”。奇怪。 - Nigel
@NigelBess 只有在你有一个抛出 NullReference 的自定义 Add() 方法时才需要这样做。否则,对象初始化语法能够知道每组大括号中都定义了一个对象,因此总是有一个值可以传递给 Add() 方法。 - Joel Coehoorn
@JoelCoehoorn 我说的是 = 左侧为空。即使集合只是像 List<int> 这样简单的东西,如果列表在构造函数完成时没有被初始化,就不会有列表可以添加,这时会抛出 NullReferenceException - Nigel
@NigelBess 在此评论时,源代码似乎会将属性的后备字段初始化为新字典(如果为空)。GitHub源代码 - Johan van Tonder
2
@JohanvanTonder:对于这种特定类型而言,那是正确的;在一般情况下,Nigel是正确的,一个看似无害的表达式,如new C { X = { 4 } }可以抛出NRE,其中 class C { public List<int> X; }。当然,这通常不是一个主要问题,因为很少有集合属性未初始化的情况,事实上,由于其他隐藏NRE的场景,这并不是唯一的陷阱。如果您不熟悉此语法实际所做的操作,仍会感到惊讶(或不愉快 :))。 - Jeroen Mostert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接