say that I have an class that has a property that is a dictionary<string,bool>, using a object initializer I can use this syntax (which I think looks pretty clean):
new MyClass()
{
Table = { {"test",true},{"test",false} }
}
however, outside of the initializer I can't do this:
this.Table = { {"test",true},{"test",false} };
Why are initializers a special case? I'd hazard a guess that it has something to do with LINQ requirements, covariance or whatnot but it feels a little incongruent not being able to use that kind of initializer everywhere...
The question is somewhat confusing, as the question has nothing to do with LINQ, nothing to do with generic variance, and features a collection initializer, as well as an object initializer. The real question is, as far as I can tell "why is it not legal to use a collection initializer outside of an object creation expression?"
The relevant design principle here is that in general, we want operations that create and initialize objects to have the word "new" in them somewhere as a signal to the reader that there is an object creation happening here. (Yes, there are a few exceptions to this rule in C#. As an exercise to the reader, see if you can name them all.)
Doing things your way makes it harder to reason about the code. Quick, what does this do?
d = new List<int>() { 10, 20, 30 };
d = { 40, 50, 60 };
Does the second line append 40, 50, 60 to the existing list? Or does it replace the old list with a new one? There's no "new" in there, so does the reader have an expectation that a new object has been created?
When you say
q = new Whatever() { MyList = { 40, 50, 60 } };
that doesn't create a new list; it appends 40, 50, 60 to an existing list allocated by the constructor. Your proposed syntax is therefore ambiguous and confusing as to whether a new list is created or not.
The proposed feature is both confusing and unnecessary, so it's unlikely to be implemented any time soon.
This limitation is far older than LINQ. Even back in C you could write
int numbers[5] = {1, 2, 3, 4, 5};
but you could not use this syntax to assign values to the array.
My guess about the reason behind this in C# is that usually you shouldn't use the same reference for two different objects. If you need to assign a new collection to an existing reference, most probably you didn't design your code very well, and you can either initialize the collection at definition, or use two separate references instead of one.
Considering your syntax throws a NullReferenceException
at runtime - are you sure you can use it?
public class Test
{
public Dictionary<string, bool> Table {get; set;}
}
public void TestMethod()
{
Test t = new Test { Table = { {"test", false} } }; //NullReferenceException
}
This compiles to the following (via reflector):
Test <>g__initLocal3 = new Test();
<>g__initLocal3.Table.Add("test", 0.0M);
As you can see, Table
isn't initialized, thus producing a NullReferenceException
at runtime.
If you create the Dictionary in the ctor of Test
, the class initializer produces a cascade of Add
statements, which is syntactic sugar in the initializer (for IEnumerable
s).
This probably wasn't introduced for normal code due to unforseen side effects we can't see or imagine. Eric Lippert might be able to help out as he probably has more insight on the matter at hand.
来源:https://stackoverflow.com/questions/4773889/covariant-object-initializers