Custom Collection Initializers
No, the compiler requires a method named Add
for the collection initializer to work. This is defined in C# specification and cannot be changed:
C# Language Specification - 7.5.10.3 Collection Initializers
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 anAdd
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 applicableAdd
method for each element initializer. [emphasis mine]
Of course, the Add
method can take more than one argument (like Dictionary<TKey, TValue>
):
dic = new Dictionary<int, int> {
{ 1, 2 },
{ 3, 4 }
};
// translated to:
dic = new Dictionary<int, int>();
dic.Add(1, 2);
dic.Add(3, 4);
Using collection initializer syntax on custom types?
I think you need to make a custom collection instead of List. Call it LookupItemTable, for example. Give that collection an Add(int, int, float, float) method and have it implement IEnumerable. For example:
class LookupItem
{
public int a;
public int b;
public float c;
public float d;
}
class LookupItemTable : List<LookupItem>
{
public void Add(int a, int b, float c, float d)
{
LookupItem item = new LookupItem();
item.a = a;
item.b = b;
item.c = c;
item.d = d;
Add(item);
}
}
private static LookupItemTable _lookupTable = new LookupItemTable {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 }
};
I've now tried the above code and it seems to work for me.
Create Dictionary-style collection initializer on custom class
To get the custom initialization to work like Dictionary you need to support two things. Your type needs to implement IEnumerable
and have an appropriate Add
method. You are initializing an Array
, which doesn't have an Add
method. For example
class PairList<T1, T2> : IEnumerable
{
private List<Pair<T1, T2>> _list = new List<Pair<T1, T2>>();
public void Add(T1 arg1, T2 arg2)
{
_list.Add(new Pair<T1, T2>(arg1, arg2));
}
IEnumerator IEnumerable.GetEnumerator()
{
return _list.GetEnumerator();
}
}
and then you can do
var temp = new PairList<int, string>
{
{0, "bob"},
{1, "phil"},
{0, "nick"}
};
C# Collection initialization syntax for use with custom matrix class?
Try this code. Your Add method must be public. Also, to make the code safe, you must add verifiers to check if the sizes of the m
matrix and the provided data match.
private int currentRow = 0;
public void Add(params double[] a)
{
for (int c = 0; c < a.Length; c++)
{
m[currentRow, c] = a[c];
}
currentRow++;
}
If you don't provide all the rows, the remaining rows will have the elements with their defaults values. Also, note that this method can be called in the future, and it would throw an exception when the m matrix has all the rows filled.
Why does a combination of object and collection initializers use Add method?
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.
Possible to mix object initializer and collection initializer?
Unfortunately it is not possible to mix object and collection initializers. The C# 3.0 specification defines an object creation expression in section 7.5.10.1 as:
object-creation-expression:
new type ( argument-listopt ) object-or-collection-initializeropt
new type object-or-collection-initializer
As you might expect, object-or-collection-initializer
is either an object initializer or a collection initializer. There is no syntax available for combining then together.
Initialize get-only collection in object initializer from existing collection
No. You can't assign this read-only property from a collection initializer. It is read-only after all.
TheList = { "A", "B" }
works since it calls Add
on TheList
(once for each item added), it doesn't create and assign a new instance, which it is not allowed to.
TheList = { existingList }
doesn't work since there is a typing issue (TheList = { existingList[0] }
does work).
The best option you have it to create a constructor parameter and drop your idea of using collection initializers for something it isn't fit for.
Using collection initializer on my own class
It should be "and", not "or".
Collection initializers are described in C# language specification, section 7.6.10.3 Collection initializers:
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.
It clearly states that the collection must implement IEnumerable
and there needs to be an Add
method. The call to the Add
method is resolved using normal overload resolution process, so it can be an extension method, generic method etc.
Related Topics
Task Parallel Library Replacement for Backgroundworker
How to Check a Input Ip Fall in a Specific Ip Range
How to Set the Timeout for a Tcpclient
How Big Is an Object Reference in .Net
Xml-Selectnodes with Default-Namespace via Xmlnamespacemanager Not Working as Expected
ASP.NET Core 2 API Post Objects Are Null
How to Get an Oauth 2.0 Authentication Token in C#
How to Detect the Language of a String
Can You Get the Column Names from a SQLdatareader
Why Should I Use Int Instead of a Byte or Short in C#
What Does {0} Mean When Found in a String in C#
What's the Difference Between Bitmap.Clone() and New Bitmap(Bitmap)
How to Compare (Directory) Paths in C#