C# Collection Initializer not honored in an object initializer [duplicate] - c#

This question already has answers here:
Why Collection Initialization Throws NullReferenceException
(4 answers)
Closed 1 year ago.
Why isn't a collection initializer honored in an object initializer?
public class Foo
{
public string[] Bar { get; set; }
public Foo() { }
}
class Program
{
static void Main(string[] args)
{
var foo = new Foo()
{
Bar = { }
};
Assert.Null(foo.Bar); // Null not empty...what gives?
}
}
The expectation is that the underlying array will be set to an initialized empty collection.

That way of initializing a new collection is rewritten internally as Bar.Add() for each element, and since you aren't adding anything to it it will not generate any calls to .Add().
If you decompile that it will look like this (notice how the initialization is lost).
private static void Main(string[] args)
{
new Foo();
}
You can try by actually adding a string to your initialization. It won't compile but show the error below.
var foo = new Foo()
{
Bar = { "" }
};
error CS1061: 'string[]' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string[]' could be found (are you missing a using directive or an assembly reference?)
Now if you initialize it with a new array it will of course work.
var foo = new Foo()
{
Bar = new[] { "" }
};
Checking this dempiled yields this.
private static void Main(string[] args)
{
Foo foo = new Foo();
string[] array = new string[1];
array[0] = "";
foo.Bar = array;
}
So in short, initialize your array in your class constructor to be safe.

It is because default(string[]) is null. You must initialize it, and you could use a constructor for it:
public class Foo
{
public string[] Bar { get; set; }
public Foo()
{
Bar = new string[0];
}
}
In this case, it will instance a new empty array on Bar property.

If a collection initializer is used as part of a member initializer, a new collection will not be instantiated. It is simply syntactic sugar for calling the Add(...) method on the collection.
Note that in this (slightly modified) example, a NullReferenceException is thrown:
public class Foo
{
public List<string> Bar { get; set; }
}
void Main()
{
var foo = new Foo()
{
Bar = { "hello" }
};
}
The C# spec defines this behaviour in section 7.6.10.2:
A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the field or property, the elements given in the initializer are added to the collection referenced by the field or property.

I am not sure why you declaring an array this way, you may do what #Felipe Oriani mentioned but I assume you know that and you just trying to understand why your way is not working as it should work. So I will try relate to this and not the best practices of the way of declaring an array.
That's an interesting question as I found some weird things while trying to understand why it happens.
I noticed that if I am initializing it in the constructor with braces, it won't compile (invalid expression term '{'). Not sure why it doesn't compile and the other ways do compile?!
public Foo()
{
Bar = { };
}
Then I mentioned that if I am declaring it not inside an object it works well:
string[] bar = { }; // Creates an empty array of strings
So it would be possible to do it and it will work as you expects:
public class Foo
{
public string[] Bar { get; set; }
public Foo(string[] bar)
{
Bar = bar;
}
}
class Program
{
static void Main(string[] args)
{
string[] bar = { };
var foo = new Foo(bar); // foo.Bar is not empty string array
}
}

Related

Why does a combination of object and collection initializers use Add method?

The following combination of object and collection initializers does not give compilation error, but it is fundamentally wrong (https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#examples), because the Add method will be used in the initialization:
public class Foo
{
public List<string> Bar { get; set; }
}
static void Main()
{
var foo = new Foo
{
Bar =
{
"one",
"two"
}
};
}
So you'll get NullReferenceException. What is the reason for making such an unsafe decision while developing the syntax of the language? Why not to use initialization of a new collection for example?
First, it's not only for combination of object and collection initializers. What you are referring here is called nested collection initializers, and the same rule (or issue by your opinion) applies to nested object initializers. So if you have the following classes:
public class Foo
{
public Bar Bar { get; set; }
}
public class Bar
{
public string Baz { get; set; }
}
and you use the following code
var foo = new Foo
{
Bar = { Baz = "one" }
};
you'll get the same NRE at runtime because no new Bar will be created, but attempt to set Baz property of the Foo.Bar.
In general the syntax for object/collection initializer is
target = source
where the source could be an expression, object initializer or collection initializer. Note that new List<Bar> { … } is not a collection initializer - it's an object creation expression (after all, everything is an object, including collection) combined with collection initializer. And here is the difference - the idea is not to omit the new, but give you a choice to either use creation expression + object/collection initializer or only initializers.
Unfortunately the C# documentation does not explain that concept, but C# specification does that in the Object Initializers section:
A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.
and
A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the target field, property or indexer, the elements given in the initializer are added to the collection referenced by the target.
So why is that? First, because it clearly does exactly what you are telling it to do. If you need new, then use new, otherwise it works as assignment (or add for collections).
Other reasons are - the target property could not be settable (already mentioned in other answers). But also it could be non creatable type (e.g. interface, abstract class), and even when it is a concrete class, except it is a struct, how it will decide that it should use new List<Bar> (or new Bar in my example) instead of new MyBarList, if we have
class MyBarList : List<Bar> { }
or new MyBar if we have
class MyBar : Bar { }
As you can see, the compiler cannot make such assumptions, so IMO the language feature is designed to work in the quite clear and logical way. The only confusing part probably is the usage of the = operator for something else, but I guess that was a tradeoff decision - use the same operator = and add new after that if needed.
Take a look at this code and the output of it due to the Debug.WriteLine():
public class Foo
{
public ObservableCollection<string> _bar = new ObservableCollection<string>();
public ObservableCollection<string> Bar
{
get
{
Debug.WriteLine("Bar property getter called");
return _bar;
}
set
{
Debug.WriteLine("Bar allocated");
_bar = value;
}
}
public Foo()
{
_bar.CollectionChanged += _bar_CollectionChanged;
}
private void _bar_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Debug.WriteLine("Item added");
}
}
public MainWindow()
{
Debug.WriteLine("Starting..");
var foo = new Foo
{
Bar =
{
"one",
"two"
}
};
Debug.WriteLine("Ending..");
}
The output is:
Starting..
Bar property getter called
Item added
Bar property getter called
Item added
Ending..
For you questions:
What is the reason for making such an unsafe decision while developing the syntax of the language? Why not to use initialization of a new collection for example?
Answer:
As you can see the intention of the designer of that feature was not to reallocate the collection but rather to help you add items to it more easily considering that you manage your collection allocation by yourself.
Hope this clear things out ;)
Consider the following code:
class Program
{
static void Main()
{
var foo = new Foo
{
Bar =
{
"one",
"two"
}
};
}
}
public class Foo
{
public List<string> Bar { get; set; } = new List<string>();
}
The compiler does not know whether you already created a new list instance within the class constructor (or in another method).
Recall that collection initializer is a series of calls to Add method on an existing collection!
See also:
Custom Collection Initializers
Also note that this initializer applies to a collection that was exposed as a property. Hence the collection initializer is possible as part of the outer object initializer (the Foo object in your example).
However, if it was a simple variable, the compiler would not let you to intialize the collection this way. Here is an example:
List<string> list =
{
"one",
"two"
};
This will throws a compilation error.
As last example, the output of the following code will be: "one, two, three, four, ". I think that now you understand why.
Pay attention to the list static instance, as well as to the private modifier in the "set" of the Bar property, which does not matters because the initializer just calls the Add method, which is accessible even when the Bar "set" is private.
class Program
{
static void Main()
{
var foo1 = new Foo
{
Bar =
{
"one",
"two"
}
};
var foo2 = new Foo
{
Bar =
{
"three",
"four"
}
};
PrintList(foo1.Bar);
}
public static void PrintList(List<string> list)
{
foreach (var item in list)
{
Console.Write(item + ", ");
}
Console.WriteLine();
}
}
public class Foo
{
private static readonly List<string> _bar = new List<string>();
public List<string> Bar { get; private set; } = _bar;
}
I believe the key thing to understand here is that there are two syntactic sugar flavors at play (or at least, there should be):
Object Initialization
Collection Initialization
Take away the List for a moment and look at the field as an object:
public class Foo
{
public object Bar { get; set; }
}
When using Object Initialization, you assign an object (or null):
var foo = new Foo()
{
Bar = new object(); //or Bar = null
}
Now, let's go back to your original example and slap Collection Initialization on top of this. This time around, the compiler realizes this property implements IEnumerable and the array you have provided is of the right type, so it attempts to call the Add method of the interface. It must first go looking for the object, which in your case is null because you haven't initialized it internally. If you debug through this, you will find that the getter gets called and returns null, hence the error.
The correct way of mixing both features then would be for you to assign a new object that you initialize with your values:
var foo = new Foo()
{
Bar = new List<string>(){ "one", "two" }
};
If you debug this version, you will find that the setter is called instead, with the new instance you initialized.
Alternatively, you can initialize your property internally:
public List<string> Bar { get; set; } = new List<string>();
If you debug this version, you will find that the property is first initialized with a value and your version of the code then executes without error (by calling the getter first):
var foo = new Foo()
{
Bar = {"one", "two"}
};
To illustrate the syntactic sugar aspect, Collection Initialization only works within the confines of a constructor calling statement:
List<string> bar = {"one", "two" }; //ERROR: Can only use array initializer expressions to assign to array types. Try using a new expression instead.
List<string> bar = new[] { "one", "two" }; //ERROR: Cannot implicitly convert type 'string[]' to 'System.Collections.Generic.List<string>'
List<string> bar = new List<string>() { "one", "two" }; //This works!
If you wish to allow initialization like in your original example, then the expectation is that the variable will be set to an instance before the Add method can be called. This is true whether you use syntactic sugar or not. I could just as well run into the same error by doing this:
var foo = new Foo();
foo.Bar.Add("one");
So you may want to initialize the variable in order to cover all bases, unless of course a null value has a semantic meaning in your application.

Changing Type at Runtime with GenericTypeArgument

This is what I am trying to get
(IList<Foo>)listPropertyInfo.GetValue(item)
This is how I get Foo type
listPropertyInfo.GetValue(item).GetType().GenericTypeArguments[0]
This is what I tried but couldn't make it successfully
Convert.ChangeType(listPropertyInfo.GetValue(item), IList<listPropertyInfo.GetValue(item).GetType().GenericTypeArguments[0]>)
and also this;
((typeof(IList<>).MakeGenericType(listPropertyInfo.GetValue(item).GetType().GenericTypeArguments.Single())))(listPropertyInfo.GetValue(item))
this is method where I am trying to implement
public static void trigger(IList<T> result)
{
foreach (var item in result)
{
foreach (var listPropertyInfo in typeof(T).GetProperties().ToList().FindAll(x => x.PropertyType.Name == typeof(IList<>).Name))
{
trigger((IList<Foo>)listPropertyInfo.GetValue(item));
}
}
}
I solved like this;
IList targetList = (IList)listPropertyInfo.GetValue(item);
Type foo = targetList.GetType().GenericTypeArguments.Single();
Type unboundGenericType = typeof(READ<>);
Type boundGenericType = unboundGenericType.MakeGenericType(foo);
MethodInfo doSomethingMethod = boundGenericType.GetMethod("trigger");
object instance = Activator.CreateInstance(boundGenericType);
doSomethingMethod.Invoke(instance, new object[] { targetList, f, properties });
If you use IList notation, Foo must be defined at compile time, you can't use expression that evaluates at runtime for Foo.
After reading your comments and and the code i would argue you are trying to do it at the wrong spot.
Here an example of how you could do this
public class MyGeneric<T>
{
public static void trigger(IList<T> result)
{
// do generic stuff where
// you do not need to know T
}
}
// this class does only explicit Foo related stuff
public class MyNONEGeneric
{
public static void trigger(IList<Foo> list)
{
// do some
}
}
class Program
{
static void Main(string[] args)
{
PersistentGenericBag<Foo> magicBag = myMagic<Foo>();
// call your generic which do some general list related stuff
MyGeneric<Foo>.trigger(list);
// call your none generic which do some foo related stuff
MyNONEGeneric.trigger(list);
}
}
like you can see i did some sort of "separation of concerns" / "single responsibility principle" here.
Every thing does only "one" thing. so if you are in need to change something you will know exactly where.
Also if you are working in a Team you can tell Person A to do the MyGeneric<T> and Person B to do the MyNONEGeneric

The difference between new List() {...} and new List {...} [duplicate]

This question already has answers here:
Why are C# 3.0 object initializer constructor parentheses optional?
(5 answers)
Closed 7 years ago.
When initializing a new List in C#, both of following will compile:
(1) List<string> s = new List<string>() { "value" };
and
(2) List<string> s = new List<string> { "value" };
What is the difference between case 1 and case 2?
This rings true for any type. Generally, you specify the () if you need to pass something to the types constructor that it doesn't expose for setting publically. If there's no parameters needed, the () are just fluff.
Consider the scenario that you may want to do some additional validation/logic to a property and you don't allow direct manipulation of it:
public class Foo
{
private string Bar { get; set; }
public string FooBar { get; set; }
public Foo (string bar)
{
Bar = bar + "foo";
}
}
So this is allowed:
var foo = new Foo("bar")
{
FooBar = "foobar"
};
Yet this isn't:
var foo = new Foo
{
Bar = "bar",
FooBar = "foobar"
};
There are some C# types that only allow you to set certain properties within the constructor.
There is no difference. It will be translated as:
List<string> s1 = new List<string>();
s1.Add("value");
List<string> s2 = new List<string>();
s2.Add("value");
From 7.6.10.1 Object creation expressions of the C# reference 5.0:
An object creation expression can omit the constructor argument list and enclosing parentheses provided it includes an object initializer or collection initializer. Omitting the constructor argument list and enclosing parentheses is equivalent to specifying an empty argument list.
The latter is called Object Initialization. You may also use MyClass m = new MyClass { MyVar = 3 };

Assignment to readonly property in initializer list

Can one tell me, why the heck does it compile?
namespace ManagedConsoleSketchbook
{
public interface IMyInterface
{
int IntfProp
{
get;
set;
}
}
public class MyClass
{
private IMyInterface field = null;
public IMyInterface Property
{
get
{
return field;
}
}
}
public class Program
{
public static void Method(MyClass #class)
{
Console.WriteLine(#class.Property.IntfProp.ToString());
}
public static void Main(string[] args)
{
// ************
// *** Here ***
// ************
// Assignment to read-only property? wth?
Method(new MyClass { Property = { IntfProp = 5 }});
}
}
}
This is a nested object initializer. It's described in the C# 4 spec like this:
A member initializer that specifies an object initializer after the equals sign is a nested object initializer - that is, an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.
So this code:
MyClass foo = new MyClass { Property = { IntfProp = 5 }};
would be equivalent to:
MyClass tmp = new MyClass();
// Call the *getter* of Property, but the *setter* of IntfProp
tmp.Property.IntfProp = 5;
MyClass foo = tmp;
Because you are using the initializer which uses the setter of ItfProp, not the setter of Property.
So it will throw a NullReferenceException at runtime, since Property will still be null.
Because
int IntfProp {
get;
set;
}
is not readonly.
You did not invoke setter of MyClass.Property, just getter.

Interface collection member strange behavior during object initialization

I run into runtime NullReferenceException exception in the following code:
public class Container
{
public IList<string> Items { get; set; }
}
class Program
{
static void Main(string[] args)
{
var container = new Container() { Items = {"Test"} };
}
}
It's logical that compiller couldn't create interaface instance but I got a runtime exception, not a compile time. I was confused even more when I investigated this behavior further:
var container = new Container() { Items = {} }; //Legal, Items is null after initialization
var container = new Container() { Items = { "Test" } }; //Legal, throws exception
container.Items = {}; //Illegal doesn't compile
container.Items = {"Test"}; //Illegal doesn't compile
Is this some kind of bug or I don't understand something?
And I'm using .net framework 4.0
It compiles, because compiler has no knowledge about is the List already initialized somewhere else. You can make it work by adding initialization into a constructor:
public class Container
{
public IList<string> Items { get; set; }
public Container()
{
Items = new List<string>();
}
}
Or changing the property to hide a field, which is initialized when class instance is created:
private IList<string> items = new List<string>();
public IList<string> Items
{
get { return items; }
set { items = value; }
}
Then, var container = new Container() { Items = { "Test" } }; works just fine.
At runtime .Add() method is called for every item in collection initializer group. When property is not initialized with new List<string> it has null value, and that's why NullReferenceException is thrown.
Object and Collection Initializers (C# Programming Guide)
By using a collection initializer you do not have to specify multiple
calls to the Add method of the class in your source code; the compiler
adds the calls.
you didn't initialize the List
var container = new Container() { Items = new List<string>() { "Test" } };
By the way the below is legal as for the compiler there is nothing wrong with it (syntax is correct,etc)
var container = new Container() { Items = {} };
but because the compiler doesn't know that Items list has not been initialized (you are not passing any item in the collection initializator {}) the .Add method won't be called on the List and the run-time won't know that the Items object is null
On the other hand the below is legal for the compiler but it throws an exception at run time because you try to initialize the the list passing an item (it is correct for the compiler for the same reason explained above) so when the run time will call the .Add method behind the scene, it will throw a null reference exception because Items has not been initialized
var container = new Container() { Items = { "Test" } };

Categories

Resources