Collection Initializers in C# - c#

In Java, I can create an List and immediately populate it using a static initializer. Something like this:
List &ltString&gt list = new ArrayList&ltString&gt()
{{
Add("a");
Add("b");
Add("c");
}}
Which is convenient, because I can create the list on the fly, and pass it as an argument into a function. Something like this:
printList(new ArrayList&ltString&gt()
{{
Add("a");
Add("b");
Add("c");
}});
I am new to C# and trying to figure out how to do this, but am coming up empty. Is this possible in C#? And if so, how can it be done?

You can use a collection initializer:
new List<string> { "a", "b", "c" }
This compiles to a sequence of calls to the Add method.
If the Add method takes multiple arguments (eg, a dictionary), you'll need to wrap each call in a separate pair of braces:
new Dictionary<string, Exception> {
{ "a", new InvalidProgramException() },
{ "b", null },
{ "c", new BadImageFormatException() }
}

Since C# 3.0 you can do it as well:
List <String> list = new List<String>
{
"a", "b", "c"
};
MSDN, Collection Initializers
Collection initializers let you specify one or more element
intializers when you initialize a collection class that implements
IEnumerable. The element initializers can be a simple value, an
expression or an object initializer. 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.
EDIT: Answer to comment regarding dictionary
IDictionary<string, string> map = new Dictionary<string, string>
{
{ "Key0", "Value0" },
{ "Key1", "Value1" }
};

Yes, it's described on MSDN here

Related

How to compare sets that are nested in dictionaries with NUnit?

In newer versions of NUnit, it is apparently possible to compare nested collections, as this answer said. However, it seems like this does not work so well with nested HashSets. Consider:
var a1 = new Dictionary<string, HashSet<string>> {
{ "a", new() { "b", "c" } },
{ "b", new() { "c" } },
{ "c", new() { } },
};
var a2 = new Dictionary<string, HashSet<string>> {
{ "c", new() { } },
{ "a", new() { "b", "c" } },
{ "b", new() { "c" } },
};
CollectionAssert.AreEqual(a1, a2);
This passes, but if I change the order in which I add values to the sets a little bit (or do anything that changes the iteration order of one of the sets):
var a1 = new Dictionary<string, HashSet<string>> {
{ "a", new() { "b", "c" } },
{ "b", new() { "c" } },
{ "c", new() { } },
};
var a2 = new Dictionary<string, HashSet<string>> {
{ "c", new() { } },
{ "a", new() { "c", "b" } },
{ "b", new() { "c" } },
};
CollectionAssert.AreEqual(a1, a2);
I expected it to still pass because a HashSet is unordered, but the test fails:
----> NUnit.Framework.AssertionException : Expected and actual are both <System.Collections.Generic.Dictionary`2[System.String,System.Collections.Generic.HashSet`1[System.String]]> with 3 elements
Values differ at index [0]
String lengths are both 1. Strings differ at index 0.
Expected: "b"
But was: "c"
-----------^
It seems like NUnit is not aware that HashSet is unordered, but is aware that a Dictionary is unordered.
So I think I need to use HashSet<string>.CreateSetComparer() somewhere. I know I can specify the equality comparer to use for a comparison with Using:
Assert.That(a, Is.EqualTo(b).Using(someEqualityComparer));
But I am trying to provide it for the comparison of the nested sets. How would I do that?
To avoid being an XY problem: The dictionary in my real code actually represents the edges of a control flow graph. It's actually a Dictionary<BasicBlock, HashSet<BasicBlock>>.
NUnit isn't exactly "aware" that dictionaries are unordered. However, it does have special code for testing Dictionary equality, as described on this page. You'll note that the page doesn't say anything about Hashsets, which get no special treatment.
As you suggested, the simplest way to deal with Hashsets in your example is to define your own comparer. I suggest defining one, which implements, IEqualityComparer<Hashset>. This will automatically take care of the nesting problem, since NUnit will only use that comparer when it reaches the nested Hashsets.
If necessary, you can repeat Using to create multiple comparers for different types. However, from what you have said, that doesn't seem to be necessary in this case.

How do I write an array-style initialiser for a class I wrote?

In C# you can initialise arrays and other objects, such as Lists, or even Dictionaries, like this:
Type[] array = new Type[] { new Type(), new Type(), new Type()...};
or, more bizarre looking, for Dictionaries:
Dictionary<string, object> dictionary = new Dictionary<string, object> () {
{ "key1", value },
{ "key2", value }
};
How would I write a constructor for my own type of object? In my case, I want to use it for an object I named LayerGroup:
LayerGroup layers = new LayerGroup() {
"background",
"foreground",
"menu",
"entities"
};
And I'd like to know how to do it in Dictionary style too:
LayerGroup layers = new LayerGroup() {
{ 4, "background" },
{ 1, "foreground" },
{ 0, "menu" },
{ 3, "entities" }
};
(In that case, I would of course have a different structure for the LayerGroup class, that's just an example)
To get collection initializer you'll need to implement 2 methods and inherit your class from IEnumerable:
public class LayerGroup: IEnumerable
{
private readonly IList<string> _collection = new List<string>();
public void Add(string value)
{
_collection.Add(value);
}
public IEnumerator GetEnumerator()
{
return _collection.GetEnumerator();
}
}
Similarly, if you want to get "Dictionary like" initialization, you need to implement
public void Add(string key, string value)
{
...
}
or whole IDictionary interface, if you want to get full spectrum of Dictionary functionality.
The array-style initializers are called Collection Initializers. In order for collection initializers to work, the object being initialized needs to implement IEnumerable, and provide the corresponding Add method, with the signature matching the parameters that you plan to supply.
Hence the LayerGroup class needs to contain an Add method that looks like this:
public void Add(string name);
This would let you initialize LayerGroup with a list-like initializer. If you provide an Add method like this
public void Add(int number, string name);
you would be able to use a dictionary-like initializer (i.e. the last snippet from your question).

Return a constant string array

I created a string[] getter to get some information on a class. I want it to always return the same value, and not create a new object on each call.
I have it implemented now like this:
string[] _someStrings = { "foo", "bar" };
protected string[] someStrings {
get {
return _someStrings;
}
}
which seem to be OK. However, my first inkling was to write it like this:
protected string[] someStrings {
get {
return { "foo", "bar" };
}
}
but that doesn't work (I get the error ; expected).
Why?
(this is mainly a "getting-to-understand-C# question).
update I made a typo. I do not want to create a new object on each call.
The correct syntax would be this:
return new [] { "foo", "bar" };
The reason is that the short syntax without new [] is only valid for an assignment.
As you correctly note in a comment, this will create a new object on every call. The only way to avoid this is with a field that stores the created instance and return that field. This is exactly the solution you already have.
Please note however, that this allows consumers to change the contents of the array and affect other consumers:
var a1 = foo.SomeStrings;
var a2 = foo.SomeStrings;
a1[0] = "Some other value";
Assert.Equal("Some other value", a2[0]); // will pass
As an alternate approach, may I suggest, if the contents are supposed to be constant, using a read-only collection instead, such that:
private readonly ReadOnlyCollection<string> UnderlyingReadOnlyStrings;
// populate the read-only collection, then...
public ReadOnlyCollection<string> ReadOnlyStrings {
get { return UnderlyingReadOnlyStrings; }
}
The benefit here is that your collection truly is read-only. And practically constant. It can't be re-assigned to, and the contents cannot be altered. You could even declare the underlying collection as static and populate in a static constructor.
Your second example doesn't work, as previously explained, because you're trying to return an "inline array", so to speak, and the syntax is not correct, and if it were, you would be newing the array each time - which goes against your requirements.
Your syntax is incorrect. Try this:
protected string[] someStrings
{
get
{
return new string[] { "foo", "bar" };
}
}
You can't have const array, but you can have a readonly one that will work as you expect (can also be static, obviously):
public readonly string[] someStrings = { "foo", "bar" };

IDictionary assignment shortcuts compiler feature or language feature?

Through some random object creation today I came across this neat little shortcut for a Dictionary<K, V>. Is the following assignment a compiler shortcut or is it a feature of Dictionary<string, string>.
IDictionary<string, string> items = { { "item1key", "item1value" } };
Looking at the source for Dictionary<K, V> I don't see anything offhand for how this works. Implementing all the interfaces for this class dot not allow me to perform a similar operation. Why is it that we can do it for a dictionary but not another type. For example, how does the compiler or language feature know that the first item is a key and the second item is the value. Or even more specific this same syntax can't be used for a List<string>
List<string> items = { "item1" };
So the first is valid, why?
I'm not necessarily trying to duplicate this but rather curious as to why it is the way it is. What makes a dictionary special in this case?
Example that works
public class Button
{
public string Title { get; set; }
public ButtonType Type { get; set; }
public IDictionary<string, string> Items { get; set; }
public bool RequiresSelected { get; set; }
}
var buttons = new List<Button>
{
new Button {
Items = {
{"button1", "Button 1"},
{"button2", "Button 2"},
{"button3", "Button 3"},
},
Title = "3 Buttons",
Type = ButtonType.DropDown
}
};
The syntax you've shown isn't valid in C#. You'd need:
IDictionary<string, string> items = new Dictionary<string, string>
{ { "item1key", "item1value" } };
At that point it's just a normal collection initializer, so the list equivalent would be:
List<string> items = new List<string> { "item1" };
EDIT: Let's see if my edit can beat yours. My guess is that you've seen something like:
var foo = new Foo {
SomeDictionaryProperty = {
{ "item1key", "item1value" }
}
};
That's an embedded collection initializer, and can be used for lists too. It's not creating a new dictionary, it's adding to an existing one. The code above is equivalent to:
var tmp = new Foo();
tmp.SomeDictionaryProperty.Add("item1key", "item1value");
var foo = tmp;
Another example of it working:
var form = new Form {
Controls = { new Label { Text = "Foo"}, new Label { Text = "Bar" } }
};
See section 7.6.10.2 of the C# 4 specification (Object Initializers) for more information. The important bit is this:
member-initializer:
identifier = initializer-value
initializer-value:
expression
object-or-collection-initializer
So you can initialize a property to either be a specific value (in which case the setter will be used) or via an object/collection initializer, in which case the getter for the property will be used, and then setters or the Add method will be used for the body of the object/collection initializer.
This is a feature of C# compiler, and the dictionary is not special: any collection that supports Add can be initialized in this way. The details are in the section 7.6.10.3 of the C# Language Specification 4.0.
The collection object to which a collection initializer is applied must be of a type that implements System.Collections.IEnumerable or a compile-time error occurs. For each specified element in order, the collection initializer invokes an Add method on the target object with the expression list of the element initializer as argument list, applying normal overload resolution for each invocation. Thus, the collection object must contain an applicable Add method for each element initializer.

Why can I initialize a List like an array in C#?

Today I was surprised to find that in C# I can do:
List<int> a = new List<int> { 1, 2, 3 };
Why can I do this? What constructor is called? How can I do this with my own classes? I know that this is the way to initialize arrays but arrays are language items and Lists are simple objects ...
This is part of the collection initializer syntax in .NET. You can use this syntax on any collection you create as long as:
It implements IEnumerable (preferably IEnumerable<T>)
It has a method named Add(...)
What happens is the default constructor is called, and then Add(...) is called for each member of the initializer.
Thus, these two blocks are roughly identical:
List<int> a = new List<int> { 1, 2, 3 };
And
List<int> temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
List<int> a = temp;
You can call an alternate constructor if you want, for example to prevent over-sizing the List<T> during growing, etc:
// Notice, calls the List constructor that takes an int arg
// for initial capacity, then Add()'s three items.
List<int> a = new List<int>(3) { 1, 2, 3, }
Note that the Add() method need not take a single item, for example the Add() method for Dictionary<TKey, TValue> takes two items:
var grades = new Dictionary<string, int>
{
{ "Suzy", 100 },
{ "David", 98 },
{ "Karen", 73 }
};
Is roughly identical to:
var temp = new Dictionary<string, int>();
temp.Add("Suzy", 100);
temp.Add("David", 98);
temp.Add("Karen", 73);
var grades = temp;
So, to add this to your own class, all you need do, as mentioned, is implement IEnumerable (again, preferably IEnumerable<T>) and create one or more Add() methods:
public class SomeCollection<T> : IEnumerable<T>
{
// implement Add() methods appropriate for your collection
public void Add(T item)
{
// your add logic
}
// implement your enumerators for IEnumerable<T> (and IEnumerable)
public IEnumerator<T> GetEnumerator()
{
// your implementation
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Then you can use it just like the BCL collections do:
public class MyProgram
{
private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 };
// ...
}
(For more information, see the MSDN)
It is so called syntactic sugar.
List<T> is the "simple" class, but compiler gives a special treatment to it in order to make your life easier.
This one is so called collection initializer. You need to implement IEnumerable<T> and Add method.
According to the C# Version 3.0 Specification "The collection object to which a collection initializer is applied must be of a type that implements System.Collections.Generic.ICollection for exactly one T."
However, this information appears to be inaccurate as of this writing; see Eric Lippert's clarification in the comments below.
It works thanks to collection initializers which basically require the collection to implement an Add method and that will do the work for you.
Another cool thing about collection initializers is that you can have multiple overloads of Add method and you can call them all in the same initializer! For example this works:
public class MyCollection<T> : IEnumerable<T>
{
public void Add(T item, int number)
{
}
public void Add(T item, string text)
{
}
public bool Add(T item) //return type could be anything
{
}
}
var myCollection = new MyCollection<bool>
{
true,
{ false, 0 },
{ true, "" },
false
};
It calls the correct overloads. Also, it looks for just the method with name Add, the return type could be anything.
The array like syntax is being turned in a series of Add() calls.
To see this in a much more interesting example, consider the following code in which I do two interesting things that sound first illegal in C#, 1) setting a readonly property, 2) setting a list with a array like initializer.
public class MyClass
{
public MyClass()
{
_list = new List<string>();
}
private IList<string> _list;
public IList<string> MyList
{
get
{
return _list;
}
}
}
//In some other method
var sample = new MyClass
{
MyList = {"a", "b"}
};
This code will work perfectly, although 1) MyList is readonly and 2) I set a list with array initializer.
The reason why this works, is because in code that is part of an object intializer the compiler always turns any {} like syntax to a series of Add() calls which are perfectly legal even on a readonly field.

Categories

Resources