covariant object initializers?

帅比萌擦擦* 提交于 2020-01-02 03:36:08

问题


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...


回答1:


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.




回答2:


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.




回答3:


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 IEnumerables).

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!