Can't understand why array initializer in object initializer cannot compiled - c#

I saw NRE when using array initializer in object initializer and there was a update and https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#extension-add-methods-in-collection-initializers but still I cannot understand.
var arr = new int[] { 1, 2, 3 } generate IL code using stelem.
But int[] arr = { 1, 2, 3 } generate IL code using RuntimeHelpers.InitializeArray. I think it is using Array.Add extension method which was talk in that answer.
But in object initializer, all array initializer generate second code.
An example,
new A() {
arr = new int[] { 1, 2, 3 }
}
Array arr is created using RuntimeHelpers.InitializingArray too. Then, doesn't it mean there isn't any problem in next code?
new A() {
arr = { 1, 2, 3 } // Compiler error!
}
Not like old version of c# compiler, it makes compiler error saying system.array does not contain a definition for Add. What's happening?
EDIT
I thought just syntax without new [] makes differences but actually more than three elements makes different IL code.

The second syntax ( { arr = {... } }) is syntax sugar for sequence of value.arr.Add(.) - presumably value.arr is not initialized in your constructor and hence NRE.
Assuming A is:
class A {
public int[] arr {get;set}
}
then
var x = new A() {
arr = { 1, 2, 3 } // Compiler error!
};
is the same as
var x = new A(); // note x.arr is default null here
x.arr.Add(1); // NRE is arr is list, can't compile for int[]
x.arr.Add(2);
x.arr.Add(3);
Fix: use list, there is no reasonable way to add elements to an array.
If using some other type that does not have .Add - implement extension method that is visible to that code.
Why version with new works:
var x = new A() {
arr = new int[] { 1, 2, 3 }
};
is equivalent* of
var x = new A();
x.arr = new int[] { 1, 2, 3 };
Note that in array initializer you can use both syntaxes to the same effect, but not in array field's initializers (All possible C# array initialization syntaxes)
int[] x = { 10, 20, 30 }; // valid, same as int[] x = new int[]{ 10, 20, 30 };
while new A { arr = { 10, 20, 30} } is not the same as new A { arr = new int[3]{ 10, 20, 30} }
*It is really a bit more complicated to satisfy rules when change must be observable - see Eric Lippert's comment below

In an expression context these are legal array values:
new int[3] { 10, 20, 30 }
new int[] { 10, 20, 30 }
new[] { 10, 20, 30 }
In a local or member variable initializer context, this is a legal array initializer:
int[] x = { 10, 20, 30 };
And this is a legal collection initializer:
List<int> x = new List<int> { 10, 20, 30 };
And, if X<T> is IEnumerable<T> and has a method Add(T, T, T), this is legal:
X<int> x = new X<int> { { 10, 20, 30}, {40, 50, 60} };
But in a member or collection initializer context this is not a legal array property initializer:
new A() {
arr = { 10, 20, 30 }
}
(Note that I summarize and comment on the rules for arrays here https://stackoverflow.com/a/5678393/88656)
The question, as I understand it, is "why not?"
The answer is "no good reason". It's simply an oddity of the C# grammar and the rules for object and collection initializers.
I many times considered fixing this oddity but there was always something better to do with my time; it's a fix that benefits basically no one because the workaround is so easy.
I conjecture that there's nothing stopping the C# team from designing, specifying, implementing, testing and shipping that feature aside from the fact that there are still about a million other features that would be a better use of their time.
If you feel strongly about it, well, the compiler is open source; feel free to propose the feature and advocate for it. Or, for that matter, implement it and submit a pull request. (After you propose the feature.)
Until the feature you want is implemented you'll just have to use one of the three "expression" forms listed above. It is not burdensome to do so.

Related

Got surprised knowing that C# compiler can infer less informative declaration

Consider the following code snippet to follow my mind chronologically. The commented statements cannot be compiled.
var data1 = new int[3] { 1, 2, 3 };
var data2 = new int[] { 1, 2, 3 };
var data3 = new[] { 1, 2, 3 };
var data4 = new[] { 1, 2, 3.0f };
The simplification done for data3 and data4 is understandable.
int[] data5 = { 1, 2, 3 };
//var data6 = { 1, 2, 3 };
Unable to infer the declaration for data6 is understandable.
var data7 = new int[] { };
//var data8 = new [] { };
//int[] data9 = new [] { };
Unable to infer the declaration for data8 is also understandable.
What I don't understand is why data9 which is more informative cannot be compiled while data10 that is less informative can be compiled.
int[] data10 = { };
//var data11 = { };
Declaring data11 that cannot be compiled is also understandable.
The cases where there is a new keyword present, are usual expressions that can be used by themselves in any context where an expression is required. You use them for declarations with assignment, but they can be used in other contexts, for example:
return new[] { 3 };
or:
Call(new[] { 3 });
and so on. In these new array expressions, the type must be clear from the expression itself. This is required even when there is a variable declaration on the left.
The case data9 therefore, with int[] data9 = new [] { }; is illegal because the expression new[] { } is illegal. It is somewhat like:
object related9 = someBoolean ? "Yes" : new Exception();
which is also illegal because the expression someBoolean ? "Yes" : new Exception() is illegal in itself, with incompatible types. There is a declaration of type object, but that does not make the right-hand side legal.
The examples data5 and data10 show a completely unrelated syntax for array variable declaration.
int[] data5 = { 1, 2, 3 };
Here the right-hand side of the = operator is not an expression in itself. This syntax requires a declaration with explicit type on the left of the = sign (so data6 is not OK). This syntax is related to object initializers and collection initializers and was introduced in the C# language together with them, whereas the new[] { ... } syntax is a bit older.
You should see this answer by Lippert, then read the official C# Language Specification.
C# is of type of language that cannot infer types from context. It means that it can extract types only from the expression outside, not vice versa.
In other words, the phenomenon you have shown is identical to the fact that the compiler cannot choose the correct overload in the following case:
int foo();
string foo();
string x = foo();
However it can choose the correct overload in the following case:
void bar(int x);
void bar(string x);
string x;
bar(x);
Edit: Servy commented that lambda expression is an exception for that rule. For example:
var a = new Func<int, bool>(a => a == 1);
the compiler cannot infer the type of "a => a == 1" without inferring it from the context.

Unpacking an array into method arguments

As you know C# supports variadic methods through the params keyword:
int Add(params int[] xs) {
return xs.Sum();
}
Which can then be called with any number of arguments you like:
Add(1);
Add(1, 2);
Add(1, 2, 3);
But say I want to call Add using an array of ints1. Is this possible and how (preferably without reflection)? I tried the following but they gave syntax errors (the syntax was pure guessing):
var xs = new[] { 1, 2, 3 };
Add(xs...); // doesn't work; syntax error
Add(params xs); // doesn't work; syntax error
1 My actual use-case is different but I thought this example would be less complicated.
Your method needs a return type:
int Add(params int[] xs) {
return xs.Sum();
}
And to call it with an array you just use the ordinary syntax for method calls:
int[] xs = new[] { 1, 2, 3 };
var result = Add(xs);
The params keyword basically just allows you to take advantage of a little syntactic sugar. It tells the compiler that when it sees
Add(1, 2, 3);
It should convert that to
Add(new int[] { 1, 2, 3});
So to do this from your code, you don't have to do anything special.
int[] parameters = new int[] { ... }
results = Add(parameters);
See the documentation for more details.
As far as I know, you can just call it with an array like you would a normal method:
Add(xs);
Nothing fancy, no params keyword on the method call, no dots.
static void Main(string[] args)
{
int[] tmp = {1, 2};
var sum = Add(tmp);
}
public static int Add(params int[] xs)
{
return xs.Sum();
}
Should work just fine..
If it's anything like Java, you can just call the method with the array as an argument.
This feature is also what makes varargs dangerous, especially if one of the vararg types is also an array...

Initializing an array of a certain type using another array (C#)

This is my question:
Let's say I have a class with such a constructor:
Class A {
private int i;
public A(int new_i) {
i = new_i;
}
}
Can I initialize an array of A with an array of int?
If so, how can I do it?
I was thinking something like:
int[] b = new int[5] { 0,1,2,3,4};
A[] a;
a = new A[](b);
Thanks!
var a = Array.ConvertAll(b, item => new A(item));
The advantage here is that it will be created with the right size, without any intermediate steps - plus it works without LINQ.
In C# 2.0 it is a bit uglier - the following is identical:
A[] a = Array.ConvertAll<int,A>(b,
delegate(int item) { return new A(item); });
The combination of var, lambdas, and improved generic type inference really shine for C# 3.0 here.
You could use LINQ:
int[] b = new int[5] { 0, 1, 2, 3, 4 };
A[] a = b.Select(x => new A(x)).ToArray();

Declaring a const double[] in C#? [duplicate]

This question already has answers here:
Why does C# limit the set of types that can be declared as const?
(6 answers)
Closed 9 years ago.
I have several constants that I use, and my plan was to put them in a const array of doubles, however the compiler won't let me.
I have tried declaring it this way:
const double[] arr = {1, 2, 3, 4, 5, 6, 73, 8, 9 };
Then I settled on declaring it as static readonly:
static readonly double[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
However the question remains. Why won't compiler let me declare an array of const values? Or will it, and I just don't know how?
This is probably because
static const double[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
is in fact the same as saying
static const double[] arr = new double[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9};
A value assigned to a const has to be... const. Every reference type is not constant, and an array is a reference type.
The solution, my research showed, was using a static readonly. Or, in your case, with a fixed number of doubles, give everything an individual identifier.
Edit(2):
A little sidenode, every type can be used const, but the value assigned to it must be const. For reference types, the only thing you can assign is null:
static const double[] arr = null;
But this is completely useless. Strings are the exception, these are also the only reference type which can be used for attribute arguments.
From MSDN (http://msdn.microsoft.com/en-us/library/ms228606.aspx)
A constant-expression is an expression
that can be fully evaluated at
compile-time. Because the only way to
create a non-null value of a
reference-type [an array] is to apply the new
operator, and because the new operator
is not permitted in a
constant-expression, the only possible
value for constants of reference-types
other than string is null.
There is no way to have a const array in C#. You need to use indexers, properties, etc to ensure the contents of the array are not modified. You may need to re-evaluate the public side of your class.
Just to point out though... Static readonly -IS NOT CONST-
This is perfectly valid and not what you were wanting:
class TestClass
{
public static readonly string[] q = { "q", "w", "e" };
}
class Program
{
static void Main( string[] args )
{
TestClass.q[ 0 ] = "I am not const";
Console.WriteLine( TestClass.q[ 0 ] );
}
}
You will need to find other ways to protect your array.
I don't know why you needed to make it either constant or readonly. If you really want to make the whole array immutable, then a simple constant/readonly keyword will not help you, and what's worse is, it might also divert you to the wrong way.
For any non-immutable reference types, make them readonly only means you can never re-assign the variable itself, but the content is still changeable. See below example:
readonly double[] a = new double[]{1, 2, 3};
...
a = new double[] {2,3}; // this won't compile;
a[1] = 4; // this will compile, run and result the array to {1, 4, 3}
Depending on your context, there might be some solutions, one of them is, if what you really need is a list of double, List a = new List() {1,2,3,4,5}.AsReadOnly(); will give you a content-readonly list of double.
The problem is that you're declaring a constant array of double, not an array of constant doubles. I don't think there is a way to have an array of constants due to the way arrays work in C#.
The compiler error tells you exactly why you can't do it:
'arr' is of type 'double[]'.
A const field of a reference type other than string can only be initialized with null.

Rhino Mocks: How do I return numbers from a sequence

I have an Enumerable array
int meas[] = new double[] {3, 6, 9, 12, 15, 18};
On each successive call to the mock's method that I'm testing I want to return a value from that array.
using(_mocks.Record()) {
Expect.Call(mocked_class.GetValue()).Return(meas);
}
using(_mocks.Playback()) {
foreach(var i in meas)
Assert.AreEqual(i, mocked_class.GetValue();
}
Does anyone have an idea how I can do this?
There is alway static fake object, but this question is about rhino-mocks, so I present you with the way I'll do it.
The trick is that you create a local variable as the counter, and use it in your anonymous delegate/lambda to keep track of where you are on the array. Notice that I didn't handle the case that GetValue() is called more than 6 times.
var meas = new int[] { 3, 6, 9, 12, 15, 18 };
using (mocks.Record())
{
int forMockMethod = 0;
SetupResult.For(mocked_class.GetValue()).Do(
new Func<int>(() => meas[forMockMethod++])
);
}
using(mocks.Playback())
{
foreach (var i in meas)
Assert.AreEqual(i, mocked_class.GetValue());
}
If the functionality is the GetValue() returns each array element in succession then you should be able to set up multiple expectations eg
using(_mocks.Record()) {
Expect.Call(mocked_class.GetValue()).Return(3);
Expect.Call(mocked_class.GetValue()).Return(6);
Expect.Call(mocked_class.GetValue()).Return(9);
Expect.Call(mocked_class.GetValue()).Return(12);
Expect.Call(mocked_class.GetValue()).Return(15);
Expect.Call(mocked_class.GetValue()).Return(18);
}
using(_mocks.Playback()) {
foreach(var i in meas)
Assert.AreEqual(i, mocked_class.GetValue();
}
The mock repository will apply the expectations in order.
Any reason why you must have a mock here...
If not, I would go for a fake class.. Much Simpler and I know how to get it to do this :)
I don't know if mock frameworks provide this kind of custom behavior.
IMHO, yield will handle this.
Link.
Something like:
get_next() {
foreach( float x in meas ) {
yield x;
}
}

Categories