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.
Related
This question already has answers here:
Reference type still needs pass by ref?
(6 answers)
Closed 1 year ago.
I'm trying to understand exactly what's happening in this code below:
static void someMethod(int[] a) {
int[] b = new int[5];
a = b;
}
static void Main(string[] args) {
int[] arr = new int[10];
someMethod(arr);
Console.WriteLine(arr.Length);
}
The thing I can't explain is why the Console.Writeline() prints 10 and not 5. Arrays in C# are reference types, so I would've thought the line a = b sets the variable a to point to an array of int[5]. Yet the Console.Writeline() prints 10. I'm at a loss to explain this value-type-like behavior of arrays. Can someone explain this?
Think of a, b, and arr like boxes you can store references in.
You store a reference in arr. You pass the reference, but not the box itself, to someMethod, which sticks that reference in its own box, a.
You then create a new object and store a reference to it in b. Then you take the reference that is inside b, and stick it inside a, replacing the reference to the passed-in array inside that box.
But this does not change the value stored in arr, which contains the originally referenced object.
You can modify the referenced object, however:
static void someMethod(int[] a)
{
a[2] = 10;
a[3] = 7;
}
static void Main(string[] args)
{
int[] arr = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
someMethod(arr);
Console.WriteLine(arr[2]);
}
You will find that the array has been modified.
Using the ref keyword, as suggested in the comments, basically tells it to pass the "box" itself. See the documentation for more information.
The assignment is writing an int pointer, not copying the array definition or its items. After assigning that pointer, the size of the array does not change. Even though it is now pointing to just 5 ints.
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.
This question already has answers here:
Passing Objects By Reference or Value in C#
(9 answers)
Closed 5 years ago.
I have a C# code as follows:
int[] A = new int[] {1, 2, 3};
fun(A);
// A at this point still says 1, 2, 3.
void fun(int[] A)
{
int[] B = new int[] {4, 5, 6};
A = B;
}
I thought all arrays are passed by reference in C#. Shouldn't A after calling fun() reflect 4, 5, 6?
The array is passed by a reference, you can see this by doing A[0] = 7; from inside another method.
That reference (held by the outer variable A), however is passed by value to the function. The reference is copied and a new variable is created and passed to the function. The variable outside the function is not affected by the reassignment to the parameter variable A inside the function.
To update the original variable you need to use the ref keyword so the parameter inside the function represents the same object as outside of the function.
int[] A = new int[] {1, 2, 3};
fun2(A);
// A at this point says 7, 2, 3.
fun(ref A);
// A at this point says 4, 5, 6.
void fun2(int[] a)
{
a[0] = 7;
}
void fun(ref int[] a)
{
int[] B = new int[] {4, 5, 6};
a = B;
}
I thought all arrays are passed by reference in C#
Actually (the reference of the original array object is passed by value) which is the usual behavior in case of reference types in C#.
Your understanding is partially correct, the reference is passed but is passed by value which means a new reference gets created which is pointing to the original array object A.
The fun(int[] A) has it's own copy of reference which is pointing to the array object which contains 1,2,3 and in the fun you create a new array object B and you are just assigning the reference of new one to your local method reference variable which of-course will not have any impact on the original A object which was passed as input to the fun.
You would need to pass it by reference if you want to reflect the changes made to A in fun to be reflected back to the original array object.
You can update the array items without passing by reference which is explained well in Scott Chamberlain's answer
Hope it Helps!
what i really wonder about is this code:
arrInt ..... is an array of integers and
listArr()...is a dynamic list of integer arrays
arrInt = {1, 2}
listArr.add(arrInt)
arrInt = {3, 4}
listArr.add(arrInt)
result:
listArr(0) = {1, 2}
listArr(1) = {3, 4}
although i was certain the result was going to be:
listArr(0) = {3, 4}
listArr(1) = {3, 4}
this would suggest that each element of listArr is a strongly typed array that is an instantiated copy of arrInt
and not a reference to arrInt nor as a value that is a reference to arrInt,
thus, you might think this a naive query, ~> are all elements of List<T> created as copies of objects?
are all elements of List created as copies of objects?
No, they're not. You're creating new arrays instead of modifying the same one.
arrInt = {3, 4} is syntactic sugar for arrInt = new [] {3, 4} which is syntactic sugar for arrInt = new int[] {3, 4}. Can you see new array allocation now?
To get
listArr(0) = {3, 4}
listArr(1) = {3, 4}
you'd need to change content of initial array:
arrInt = {1, 2};
listArr.add(arrInt);
arrInt[0] = 3;
arrInt[1] = 4;
listArr.add(arrInt);
That would result int the same values in both listArr[0] and listArr[1].
Without an extended conversation, it's not actually possible to know for sure what part of this sequence of statements is confusing you. But it seems to me that the crucial aspect of these operations that you're getting wrong is probably this: when the statement listArr.Add(arrInt); is executed, the thing that is added to the list is a reference to the object that arrInt is referencing, and not the variable arrInt itself as you seem to think.
Once the statement has completed, the element in the list has no connection whatsoever to the variable arrInt. That variable contains a value, which is the reference to the array { 1, 2 }. When you execute listArr.Add(arrInt);, the thing that is added to the list is the value that the variable contains, i.e. the reference to that array.
When you assign a new value to the variable arrInt, i.e. the reference to a completely different array { 3, 4 }, the variable itself takes on that new value. But since the element in the list was just the previous value of the variable, not anything to do with the variable itself, it's unaffected.
I have a class, which its constructor is below
public GroupsForm(ref Dictionary<string, List<string>> groupsList)
I want to use the groupsList in other functions as well so I declared a private member in the class:
private Dictionary<string, List<string>> _groupsList;
But the problem is when I do _groupsList = groupsList; it makes a copy of the groupsList and changes made to _groupsList doesn't change groupsList, as I think it makes a deep copy by default. What I want is that it should point to that list.
In C++, this can easily be done via pointers, but can anyone tell me how can I do this in C#?
Ahmed has posted this image:
There are value types and reference types in C#. A dictionary is a reference type so whenever assigning a variable that holds a reference type to another, the reference is copied, which means that both variables will be holding the same data. So, if you change the data, you should expect it to change on both variables:
List<int> list1 = new List<int>() { 1, 2, 3 };
List<int> list2 = list1;
list2.Add(10);
list1.ForEach(x => Console.WriteLine(x)); // should print 1, 2, 3 and 10.
However, if you reassign the variable, then you are not changing the data:
List<int> list1 = new List<int>() { 1, 2, 3 };
List<int> list2 = list1;
list2 = new List<int>() { 10, 11, 12 };
list1.ForEach(x => Console.WriteLine(x)); // should print 1, 2, 3 only
One thing that caught my attention in your code though was that the constructor is expecting a ref. This means that you are passing the reference itself by reference, which in C++ would be something like a double pointer (type**). You'd do this if, in the snippet above, you want to have this effect:
void MethodA(ref List<int> list)
{
list = new List<int>() { 10, 11, 12 };
}
// ...
List<int> list1 = new List<int>() { 1, 2, 3 };
MethodA(ref list1);
list1.ForEach(x => Console.WriteLine(x)); // should print 10, 11, 12
One more thing -- AFAIK C# doesn't implement deep copies in any of it classes. You have to do it yourself.
In your InitGroups method, you've got an assignment statement to _groupsList - it's this new list that contains 3 items. You could change InitGroups to do something like:
var newGroups = (Dictionary<string, List<string>>)ser.ReadObject(reader,true);
foreach(var kvp in newGroups)
{
_groupsList.Add(kvp.Key,kvp.Value);
}
And then you'll still be working with the same Dictionary object.
You don't need the ref in your method signature. Objects are passed by reference anyway.
And no, there is no copying going on.
But the problem is when I do _groupsList = groupsList; it makes a copy of the groupsList
No, it does not. It copies a pointer. List is a reference type.
as I think it makes a deep copy by default
Think again. Your conclusion is wrong.
List<T> is a reference type, so all variables are pointers. Beginner C#, first week, first day, first hour. Go back and read the introduction then look for the error in the rest of your code you do not show us.
You are wrong on several points.
C# references are totally different from C++.
In short, you should not use ref here because it's for propagating assignments made to parameter to the calling code which is not what you mean.
You are wrong assuming there's any kind of copying involved here.
groupsList and _groups ist are two references pointing to the same object in memory.
You'll need to read more about reference and value types in C#.