This question already has answers here:
Why can I not modify the result of an unboxing conversion?
(3 answers)
Closed 7 years ago.
Let's look at the following code.
struct SPoint
{
public int x;
public int y;
public SPoint(int x, int y)
{
this.x = x;
this.y = y;
}
}
class Test
{
public static void Main()
{
SPoint s = new SPoint(3, 4);
object o = s;
((SPoint) o).x = 5;
}
}
Why isn't the last assignment possible? What is the reason for such behaviour?
Since s is a struct (a.k.a: a value type), (SPoint)o is a copy of the data:
From the C# language spec (§1.3, "Types and Variables"):
When a value of a value type is converted to type object, an object instance, also called a “box,” is allocated to hold the value, and the value is copied into that box. Conversely, when an object reference is cast to a value type, a check is made that the referenced object is a box of the correct value type, and, if the check succeeds, the value in the box is copied out.
The language protects you from changing the data of the cloned value type without putting it in a variable first, since you might think that you are changing the original s.x value while you are changing it's temporary (not - variabled) clone, unlike unsafe languages like C++/CLI where this kind of assignments might be allowed.
If you want, you are able to explicitly create a new variable and do your manipulations in it:
SPoint cloneOfS = ((SPoint)o);
cloneOfS.x = 5;
See MSDN:
The result of an unboxing conversion is a temporary variable. The compiler prevents you from modifying such variables because any modification would go away when the temporary variable goes away. To fix this, declare a new value-type variable to store the intermediate expression, and assign the result of the unboxing conversion to that variable.
Related
This question already has answers here:
What is the difference between a reference type and value type in c#?
(15 answers)
Closed 1 year ago.
When I passed my object to a function in c# and change its values, the originals instance values are changed? I have been looking into why this happens but no one is explaining.
class Program
{
static void Main(string[] args)
{
Class1 obj = new Class1();
obj.x = 10;
test(obj);
Console.WriteLine(obj.x); // prints 200
}
static void test(Class1 a)
{
a.x = 200;
}
}
When I tried this with the int data type, it did not change.
I have a deep understand of pass my value and reference. But in c#, I am quite confused on what is going on?
I want to know why c# changes original obj function value. It should still print 10.
When you tried it with int, a copy of the int variable was passed. For the time you ran the method your memory contained two ints, you modified one which was then thrown away
Had you passed the int with ref the original int would have been passed and the method would have modified the original value
When you pass objects, a reference to the object is passed and whether you use ref or not you can modify properties of the object and the calling method will see those modifications. The difference with using ref or not on an object is that with ref you can swap the object for a whole new one (with new) keyword and the original method will get the new object. Without ref, the original method will retain the original object because the reference that was passed (and subsequently changed for a new object) was a copy
Forget calling a method for a moment, let's do it simpler:
int x = 0;
int y = x;
y++;
What value do you now expect x to have?
It has 0; y is a copy of the value x has
Person p = new Person(){ Name = "John" };
Person q = p;
q.Name = "Mary";
What name do you expect p now to have? (It's Mary)
This is like what is happening with your posted code, so you already understand it in the context of not calling a method - y is a copy of x (and they refer to different data in memory, changing one does not change the other), q is a copy of a reference to p (but they both refer to the same data in memory, changing the data means both references see the change)
This question already has an answer here:
Why do I have to copy "this" when using LINQ in a struct (and is it OK if I do)?
(1 answer)
Closed 7 years ago.
Apparently you cannot use the keyword "this" in a method in a struct.
Note that in the below example I do not explicitly type "this" but it is implied when I reference the properties "X" or "Y".
My struct:
public struct Coord
{
public int X;
public int Y;
public Coord(int x, int y)
{
X = x;
Y = y;
}
// some other methods omitted
public List<int> GetPossibles()
{
return LaurenceAI.Values.Where(n => LaurenceAI.Possibilities[X, Y, n]).ToList();
}
}
usage example:
foreach(int numeral in targetCoord.GetPossibles())
{
//do stuff
}
Error:
Error 1 Anonymous methods, lambda expressions, and query expressions inside structs cannot access instance members of 'this'. Consider copying 'this' to a local variable outside the anonymous method, lambda expression or query expression and using the local instead. C:\Projects\Sodoku\SodokuSolver\Sodoku\LaurenceAI.cs 367 74 Sodoku
Questions:
What is the technical reason that a method in a struct cannot use keyword "this"?
Is there an elegant way to fix this so that I don't have to type out the reflection code longhand every time I want a list of possible numerals for a given Coord?
The reason is that structs are passed by value, not by reference. Using this in this context results in typically not what you want - it'll access the this pointer of the copy, not the outer object, and it'll be really confusing when any assignments you make aren't showing in the outer call. It's annoying in this specific circumstance, but in general it's stopping more weird errors.
The error message actually gives you a fairly reasonable solution - copy the values first. Do something like:
public List<int> GetPossibles()
{
var localX = X;
var localY = Y;
return LaurenceAI.Values.Where(n => LaurenceAI.Possibilities[localX, localY, n]).ToList();
}
I started my journey with C# but I realised that I have some problems with some basic information about memory when it comes to declaration of variables. See if I am correct.
int x; // I declared variable of type int, which name is x. Compiler will provide memory for it but we dont have known value of it.
x=10; // Now memory location is still the same but value now kept there is 10;
public struct Point {
public int x, y;
}
Now I define a struct named Point. Beacuse struct is a value type, it again has reserved memory for it on the computer. Howewer x and y have no value.
Now Point p1 = new Point(); // what is happening here? Struct is not a reference type. So is this just initialization of Point variable with the default constructor without assigning values to x and y?
Second short question. When I write a code like:
int x = 10;
Can I say that I created instance of class integer which value is 10 and name x;
I would be grateful for help.
// what is happening here? Struct is not a reference type. So is this just initialization of Point variable with the default constructor without assigning values to x and y?
No; there are 4 possible scenarios here:
a class: the memory space is wiped to all 0s, then any custom constructor is invoked, which may also involve field initializers
a struct called without a custom constructor: the memory space is wiped to all 0s
a struct called with a custom constructor: the custom constructor is required to assign all the fields
a struct variable used without ever calling a constructor: this is actually a thing, but the calling code must write to all the fields before they can do anything else with it; since most structs do not expose their fields, this rarely works
Second short question. When i write a code like:
int x = 10;
Can i say that i created instance of class integer which value is 10 and name x; I would be grateful for help.
Not really, because in C# terms, int is not a class (it might be in IL terms). Simply: you have declared a local variable of type int with name x and assigned it the value 10, if this is in a method. If this is a class field, then: you have declared a private instance field of type int named x with a field-initializer giving it the value of 10.
Incidentally, you should avoid public fields in general, and mutable fields on structs. You might prefer:
public struct Point {
private readonly int x, y;
public int X { get { return x; } }
public int Y { get { return y; } }
public Point(int x, int y) { this.x = x; this.y = y'; }
}
This will avoid a huge range of problems.
In C# the default struct constructor sets the struct memory to 0, effectively setting all variables to their default values.
In case of ints, it will be 0. For reference types, it will result in null.
(in other words, for any type T it will be default(T)).
Note that when you write a custom constructor in a struct, you must initialize all member fields.
When you write
int x;
this is similar to
Point p1 = new Point(); (considering Point structure is already defined)
in both the cases all integer variables will have default value of 0 and not null, which is is basically what is used in C# to denote 'nothing' and can be assigned only to reference types.
As well, in c# everything is a class, so when you write
int x = 10;
you are creating an instance of class Int32, though the run time will handle this as value type instead of ref type, as special case.
Same is true for other basic types like, Long, DateTime and few others
i know that struct is of value type and class is reference type but when i execute the following code why im getting the two different answer
can any one explain a bit
[struct|class] values {
public int x, y;
public values (int x, int y) {
this.x = x;
this.y = y;
}
}
values v = new values(12, 13);
object o = v;
v.x = 2;
Console.WriteLine(((values)o).x);
Outputs
when
it is class : output is 2
it is struct :output is 12
Can any one explain me?
Thanks
The one line that behaves very differently for struct or class is
object o = v;
When Values is a reference type, o becomes a copy of the reference v. There still is only 1 instance of Values.
When Values is a value type, o becomes a reference to the boxed copy of the instance itself. In this case the assignment creates a 2nd instance and you execute v.x = 2 on the original. The copy is not affected.
Your example includes boxing and that is an unnecessary complication, when you use values o = v; you will get the same output. The line then creates a normal copy (2nd instance) without boxing.
To sum it up: the main difference between value and reference type is in copy semantics. You will notice different behaviour in simple assignments (x = y) and in parameter passing (foo(x)).
You can expect gotchas with mutable value types. As an exercise, see what f.Myvalue.x = 2; does with Values as class or struct with
class Foo { public Values MyValue { get; set; } }
When you use a structure and you assign it (or use it as a parameter of a method), you have a brand new copy of your structure.
Whereas, with a class, you assign a reference to the class.
Key line is
object o = v;
When values is struct (or value-type), it causes boxing values. According this (following that link, you can find exactly your question at end :))
Boxing a value of a value-type consists of allocating an object
instance and copying the value-type value into that instance.
So, your value in v is copied. When you unboxing here
Console.WriteLine(((values)o).x);
you get original v value, not v after
v.x = 2;
So, answer for struct (or value-types) is 12.
For class (or reference-types) it's very simple. You are not boxing, just cast to object, so next you are working with this original v and change it value.
Given
SomeStruct s1, s2, s3;
Object o1;
... s1 gets a value somehow, then...
s2 = s1; // Q1
o1 = s1; // Q2
s3 = (SomeStruct)o1; // Q3
statement Q1 will mutate structure s2 by overwriting all of its public and private fields with the values of the corresponding fields in s1. Statement Q2 will generate a new heap object which will act as though it contains a field of type SomeStruct, and will modify that field by overwriting all of its fields with the corresponding fields in s1. Statement Q3 will check whether o1 contains a "boxed" SomeStruct and, if so, will overwrite all the fields of s3 with the corresponding ones from the boxed object.
Note that boxed structures are not usually modified while they sit within heap objects. If one were to say:
var temp = (SomeStruct)o1;
temp.someField = Whatever;
o1 = temp;
that sequence would create a new heap object which would hold a modified copy of the original structure, but the original boxed structure would remain as it was. There are, however, ways a structure within a boxed object can be modified. One should in most cases avoid using such techniques, but one should be aware there's no such thing as a non-trivial immutable structure (a struct with no fields would be immutable, but not very useful). In general, if one wants to be able to refer to a mutable instance of a structure type, it's best to encapsulate it within a simple class object, e.g.
class ExposedFieldHolder<T>
{
public T Value;
public ExposedFieldHolder(T v) {Value = v;}
}
and if one wants to have an immutable instance, one should encapsulate it in an immutable class object
class ImmutableHolder<T>
{
T value;
public T Value { get { return value; } ;
public ImmutableHolder(T v) {value = v;}
override public bool Equals(Object o) {
var other = o as ImmutableHolder<T>;
if (!other) return false;
return Object.Equals(value,other.value);
}
override public int GetHashCode() { return Object.GetHashCode(value); }
}
struct Point
{
public int x;
public int y;
}
void Main()
{
Point p;
p.x = 1;
p.y = 1;
Object o = p;
((Point) o).x = 4; // error
((Point) o).x = 5; // error
((Point) o).x = 6; // error
p = (Point) o // expect 6
}
Why doesn't it compile to
ldloc.1 // o
unbox Point
ldc.i4.4
stfld Point.x
Where C++ CLI allows it.
For those who don't know, unbox is not required to create a copy of value types, instead it pushes a pointer to the value on to the stack.
Only assignment would create a copy.
Because of how value types work, the boxed Point is a copy of the original, and "unboxing" it by casting back to Point creates yet another copy. From the C# language spec (§1.3, "Types and Variables"):
When a value of a value type is converted to type object, an object instance, also called a “box,” is allocated to hold the value, and the value is copied into that box. Conversely, when an object reference is cast to a value type, a check is made that the referenced object is a box of the correct value type, and, if the check succeeds, the value in the box is copied out.
Modifying the copy wouldn't change the original anyway, so it wouldn't make much sense to allow it.
As for C++...well...of course, the rules of C# don't necessarily apply to it. :) The CLR actually has quite a bit more flexibility with pointers and references than you'd first think, and C++ -- being known for such flexibility -- probably takes advantage of it.
You can't do this, because the result of unboxing is a copy of the boxed value, not the boxed value itself. And casting object to a value type is the definition of unboxing. So, if the compiler allowed you to do this, it would be very confusing, because the assignments wouldn't actually do anything.
I think the reason your code works in C++/CLI is because that language in general has more support for working (or not) with references, including strongly-typed boxes (e.g. Point^) and treating (some) classes as value types (e.g. using MemoryStream without ^).
You can accomplish this using the System.Runtime.CompilerService.Unsafe.Unbox function:
static void Main()
{
Point p;
p.x = 1;
p.y = 1;
Object o = p;
Unsafe.Unbox<Point>(o).x = 6; // error
p = (Point)o; // 6
Console.WriteLine(p.x);
}
As the documentation notes, and I presume the reason it is considered unsafe, is that you must not do this with an immutable built-in type [e.g. Unbox<int>(i) = 42], and the system does nothing to enforce this.