Lately while going through the code of latest MEF release, I stumbled upon a piece of code that used a very little known feature of .NET
Just take a look:
public struct Tuple<TFirst, TSecond>
{
public Tuple(TFirst first, TSecond second)
{
this = new Tuple<TFirst, TSecond>();//looks strange?
this.First = first;
this.Second = second;
}
//[...]
}
I myself was shocked at first. “Hey! You can’t assign to this! It’s against the nature and common sense.” Then I went to C# language specification to look for explanation and indeed I’ve found it.
The key here is the fact that Tuple is a struct, not a class. As it turns out, the meaning of this differs between those two.
The explanation comes from chapter 11.3.6 – Meaning of this
Within an instance constructor or instance function member of a class, this is classified as a value. Thus, while this can be used to refer to the instance for which the function member was invoked, it is not possible to assign to this in a function member of a class.Within an instance constructor of a struct, this corresponds to an out parameter of the struct type, and within an instance function member of a struct, this corresponds to a ref parameter of the struct type. In both cases, this is classified as a variable, and it is possible to modify the entire struct for which the function member was invoked by assigning to this or by passing this as a ref or out parameter.
It makes total sense when you think about it, still – I think many experienced people can be surprised by that.
Comments
this is indeed an interesting find !
Hmmmm, so why would you want to do this? What purpose does it serve in the code snippet you provided?
Is it not implicit that the struct has already been initialised with a default-constructed instance before entry into that constructor?
@Daniel
Well, here’s the catch – remove the first line, and then try to compile.
Oh, and First, and Second are properties, not fields.
Interesting way to work around a bug – but wouldn’t it be simpler to just chain the constructors as you should …
public Tuple(TFirst first, TSecond second) : this() { … }
Then everything works the way it should without needing any kung fu!