2

Example:

enter image description here

I deleted the new[] type that was listed before the first curly bracket and saw that this code still functions as expected. I've tried replicating this for some other interfaces and types. I'm curious to know what is going on in the background and how/why this syntax works, but I'm not quite sure which keywords to search use in my searches.

Dinerdo
  • 560
  • 7
  • 27
  • Actually, that seems to result in a NullReferenceException at runtime. I'm guessing then that it doesn't really work, despite compiling (to what I presume are [a series of .Add() calls](https://stackoverflow.com/questions/37873193/property-initialization-does-not-call-set-for-listt/37873449#37873449) rather than the creation of an array as you might expect from new[]). – BoltClock Oct 23 '18 at 18:11
  • It definitely works for me. I'm using SolrNet, and I can see it using those results when the query string is generated based on this information. – Dinerdo Oct 23 '18 at 18:17
  • Can we see the entire statement this initializer appears in? – BoltClock Oct 23 '18 at 18:18
  • Looking at the source, I'm guessing it works because it's a List underneath? https://github.com/SolrNet/SolrNet/blob/master/SolrNet/Commands/Parameters/CommonQueryOptions.cs – Dinerdo Oct 23 '18 at 18:21
  • Ah, I see that it's being initialized as a List in the constructor. – BoltClock Oct 23 '18 at 18:23
  • 1
    I knew { } resulted in a call to .Add but hadn't put that together or (derp) thought to look a the source until you asked that. So this explains why I couldn't replicate it. I wasn't initializing the underlying list in my examples, so .Add would fail on the first attempt as I would expect if the list was not initialized. – Dinerdo Oct 23 '18 at 18:28

2 Answers2

2

From the source that you've linked to, we see that the ICollection<string> Fields property is being initialized in the constructor as a List<string>:

/// <summary>
/// Common, shared query options
/// </summary>
public CommonQueryOptions() {
    Fields = new List<string>();
    FilterQueries = new List<ISolrQuery>();
    Facet = new FacetParameters();
    ExtraParams = new Dictionary<string, string>();
}

/// <summary>
/// Fields to retrieve.
/// By default, all stored fields are returned
/// </summary>
public ICollection<string> Fields { get; set; }

This allows the initializer syntax to function as expected, populating the now-initialized List with values.

What Fields = new[] { "*", "score" } does is replace that List with a string array containing those values. This works because arrays also implement ICollection<T>.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
1

Please consider following example:

    public class UnitTest
{
    [Fact]
    public void TestList()
    {
        var sample = new SampleList
        {
            Fields = { "b", "c" }
        };

        var count = sample.Fields.Count;
        Assert.Equal(2, count);
    }

    [Fact]
    public void TestArray()
    {
        var sample = new SampleArray
        {
            Fields = { "b", "c" }
        };

        var count = sample.Fields.Count;
        Assert.Equal(2, count);
    }
}

public class SampleList
{
    public ICollection<string> Fields { get; set; } = new List<string>
    {
        "a"
    };
}

public class SampleArray
{
    public ICollection<string> Fields { get; set; } = new string[]
    {
        "a"
    };
}

The syntax that you've used works only in object initializer and it tries to add specified items to collection.

  • In the TestList() test case assert fails because it adds "b", "c" to the list and results in "a", "b", "c"

  • In the TestArray() test case assert fails because it throws following exception

System.NotSupportedException: 'Collection was of a fixed size.'

It means that if you want to use this object initialization syntax you have to use collections that don't have fixed size.

Szymon Tomczyk
  • 1,219
  • 11
  • 16