3

What is the name of the syntax used in the Headers property? Headers is defined as public HttpRequestHeaders Headers {get;}. It hurts my head that the left side of the expression is not a setter.


I'm not finding it in hidden features of c# or History of 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" })
};
9
  • why would it be a setter, this is simply an initialization list setting the Headers property Commented Mar 2, 2022 at 21:33
  • 4
    You are probably looking for this page: Object Initializers with collection read-only property initialization Commented Mar 2, 2022 at 21:35
  • Does it "hurt your head" that readonly int foo = 5; is valid, even though it's "readonly"? Same concept. Commented Mar 2, 2022 at 21:45
  • Does this even compile? What type of object is being assigned to Headers? Commented Mar 2, 2022 at 22:07
  • @KirkWoll readonly int foo = 5; - no this is a field being assigned to during class initialization (before construction). Setting a field is ok to me. Disagree that it's the same, because the property here has no setter. Commented Mar 3, 2022 at 18:47

2 Answers 2

1

This is Object Initializers with collection read-only property initialization.

It hurts my head that the left side of the expression is not a setter.

Using a collection initializer you can set the value of a property during the constructor even with no set defined. Specifically for a collection, the documentation linked above says this:

Collection initializers let you specify one or more element initializers when you initialize a collection type that implements IEnumerable and has Add with the appropriate signature as an instance method or an extension method.

This can be really strange (or cool, depending on how you look at it), because you can build your own Add() extension method for almost any IEnumerable type and use it to do some really interesting things.

Also remember, when using a property that is also a collection or other property, you do not need a set to call methods or change property values within this object or collection. Instead, you first get the reference to the collection, and then use whatever access is provided by that nested property.

For example, let's say you have a variable tokenRequest that has a property Headers of type HttpRequestHeaders. The HttpRequestHeaders type in turn has an Add() method. To call that method you first get the reference to the Headers property and the call method on that reference. No set is ever used or needed, and yet you still managed to change a property that only has get defined.

Collection initializers take advantage of this.

Sign up to request clarification or add additional context in comments.

3 Comments

'When using an object initializer, you can set the value or property during the constructor even with no set defined.' This explanation is not correct. Set is not being called. As explained in the "Object Initializers with collection read-only property initialization" section of the document you linked, leaving out new in the collection initializer uses the getter to the collection to call Add for each item in the braces
@NigelBess Yes, I know, I'm fixing it. I re-read the initial post and realized it was misleading. It's slightly better now, but still more edits coming in.
@JoelCoehoorn suggestion: since the article this heading, "Object Initializers with collection read-only property initialization", would you mind changing the link to that title, and linking to that heading? learn.microsoft.com/en-us/dotnet/csharp/programming-guide/…
1

I'm not sure if there's an official name for it, but I've seen it being called collection initializer "duck typing", done implicitly by the compiler. The compiler will look for a suitable Add method implemented by the type, as well as the type has to implement the IEnumerable interface. HttpRequestMessage.Headers ultimately fits both these criteria, and even though the property has only a getter, the compiler will translate the collection initializer into consecutive "Add" calls right after the object has been created.

5 Comments

Wow that is some horrible design. That means the left hand side of the = is susceptible to a NullReferenceException. Weird.
@NigelBess Only if you have a custom Add() that throws the NullReference. Otherwise, the object initializer syntax is able to know an object is defined for each set of braces, so there's always a value there to pass to the Add() method.
@JoelCoehoorn Im talking about the left-hand-side of the = being null. Even if the collection is something simple like List<int>, if the list is not initialized by the time the constructor completes there will be no list to which to add, and a NullReferenceException gets thrown
@NigelBess The source (at the time of this comment) seems to initialize the property backing field to a new dictionary if it is null. github source
@JohanvanTonder: that's true for this particular type; in the general case Nigel is correct that a seemingly innocuous expression like new C { X = { 4 } } can throw an NRE, with class C { public List<int> X; }. Of course this is generally not a major problem because it would be rare to leave collection properties uninitialized, and indeed because of other scenarios where NREs lurk this is hardly the only pitfall. It can still be surprising (or distasteful :)) if you're not familiar with what this syntax actually does.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.