Reputation:
I was recently looking into internals of CancellationToken
structure and discovered a bit of weird construct (to be more precise, assignment of value to this
keyword).
Code of one of its constructors is as following:
public CancellationToken( bool canceled )
{
this = new CancellationToken();
if ( canceled )
{
this.m_source = CancellationTokenSource.InternalGetStaticSource( canceled );
}
}
What is the meaning of line on which the assignment to this
keyword occurs?
Please note that assignment to this
keyword is not possible for classes - error Cannot assign to '<this>' because it is read-only
occurs.
Upvotes: 7
Views: 2396
Reputation: 9027
This is a very little known feature of C# - this allows a struct to overwrite its own data.
As far as practical application goes, you're not going to find many uses for this..
struct MyStruct
{
int a = 1;
int b = 2;
int c = 3;
public void Mutate()
{
a = 10;
b = 20;
c = 30;
}
public void Reset()
{
a = 1;
b = 2;
c = 3;
}
public void Reset2()
{
this = new MyStruct();
}
// The two Reset methods are equivilent...
}
Thinking about it more, there's a fundamental difference in what "this" means when you're dealing with value types vs reference types.
When you call "this" on a reference type - what you get is a pointer that lives on the stack, you don't actually get the object itself. The pointer implicitly dereferences back to the object on the heap, which abstracts the indirection. Now if assigning to this
in classes were possible, and you'd have said something like this = new MyReferenceType()
, you'd have changed the pointer to point to a different heap object in the current scope - you wouldn't have changed the original object itself in the heap, nor would it have caused any other references/pointers to refer the new heap object. Its very likely that as soon as your mutated pointer would have gone out of scope - the new heap object you'd have created would have been subject to garbage collection.
When you call "this" on a value type - you are getting the actual object, not a reference or pointer. There is no indirection so you are free to overwrite the raw bits at this memory location (which is exactly what the default constructor does).
Upvotes: 6
Reputation: 5128
Just a guess:
Every class is a reference type meaning that the memory is allocated in the heap and the caller gets access to the actual data through the pointer. For example:
Customer c1 = new Customer('CUSTID'); // "Customer" is a reference type
Customer c2 = c1; // "c1" and "c2" points to the same memory within the heap
Every struct is a value type meaning that the memory is allocated in the stack and the caller deals with the actual instance rather than with the reference to that instance. For example:
Customer c1 = new Customer('CUSTID'); // "Customer" is a value type
Customer c2 = c1; // New memory gets allocated for "c2" within the stack
Considering your example:
this = new Customer();
Performing the following operation on a struct simply initializes it with zero values:
mov eax,dword ptr [ebp-3Ch] ; Save pointer to "ebp-3Ch" in EAX register
xor edx,edx ; Clear EDX register
mov dword ptr [eax],edx ; Write "zero" by address containing in EAX
I don't know why it's not possible with reference types but my guess is that in will require traversing of the entire object graph to "reset" it completely (which might be not an easy task). I assume that this will become worth in case of circular references.
Again, this is just my thoughts and I'd very much like someone to either prove or discard (with an explanation, of course) them.
Upvotes: 1