Tuesday, December 03, 2024 01:58

Table of contents >> LINQ > The meaning of “this” on extension methods

The meaning of “this” on extension methods

In the lesson about extension methods, we learned that they are a nice way of adding extra functionality to existing types, and we did that by adding a static method inside a static class, that took a type parameter prefixed with the this keyword, like this:


So, although my original Book class does not contain a method called SellBook(), I am able to call it inside the Main() method because I declared an extension method for the Book class, and this static SellBook() method took a Book type parameter prefixed with the this keyword, that signals the compiler that the method is intended as an extension method for the Book type.

But why the this keyword? What does it mean, what does it represent?

Let’s recreate the above example without using an extension method:

See code changes


Legend:

  • green lines with a plus near the line numbers are newly added lines
  • red lines with a minus near the line numbers are removed lines



The output is this:

Now, when I’m instantiating a new Book in my Main() method, think about what the new operator has to do: it has to go on the Heap memory and allocate enough RAM memory for a Book. Well… how big is a Book? If we look inside it, we can see that each Book has its own copy of the copiesSold int and the bookName string fields. If we declared them as static, they would be shared amongst all instances of Book, but we didn’t. So, in reality, a Book is the size of an int plus the size of a string. You might be wondering about the extra size that needs to be allocated for the SellBook() method of each of the Book instances, but here is the tip of the day: ALL METHODS IN C# ARE STATIC. Even the instance ones, where we don’t place the static keyword on them, they just look like instance methods! This is because it makes no sense for the compiler when I say new Book() to actually make a copy of the SellBook() method, because the code inside it is identical for any Book instance. It doesn’t change from instance to instance.

I will prove this to you, but before that, you should know that on instance methods, the compiler implicitly gives you the this keyword on each non static member you use. So, yes, we can write the SellBook() method like this:

but the compiler will implicitly add the this keyword on all non static members used inside it:

But now, we will take the control away from the compiler and we will turn our SellBook() from an instance method to a static method. Of course, from the lesson static members we already know that we cannot access instance members inside static methods, so we also need to pass a Book parameter to the SellBook() method, in order for it to know to which Book we are referring to:

See code changes


Legend:

  • green lines with a plus near the line numbers are newly added lines
  • red lines with a minus near the line numbers are removed lines


Obviously, inside the Main() method I can no longer syntactically call the SellBook() method like that, I have to call it through the class itself, because it belongs to the class blueprint, not its copies.

The result is the same:

So, I am just being more explicit, instead of relying on the compiler to pass the this instance, I am passing the instance myself. And since each instance of Book has its own copy of the non static members copiesSold and bookName, if I pass different instances of Book as parameters to the static SellBook() method, each of these instances will have their copy of copiesSold variable incremented, independently one from another. I can create another Book instance and sell it through the same static method, and still have individual results:

See code changes


Legend:

  • green lines with a plus near the line numbers are newly added lines
  • red lines with a minus near the line numbers are removed lines


If we run the code now:

notice that the “The three Musketeers” book has its own copy of copiesSold, which is incremented separately.

So, hopefully, that’s making sense: all methods are really static, the compiler just adds the this keyword for us.

However, you have to admit, the syntax for selling a book in this case is not really nice, because we have to say Book.SellBook(firstBook), instead of just saying firstBook.SellBook(). And this is the beauty of extension methods, in fact, this is the reason why we say this Book in extension methods parameters, this is the reason why the designers of the C# language decided to use this, because, really, it is the this argument that you are giving it on. If I modify my code to convert the SellBook() method into an extension method, I can still call SellBook() as if it was an instance method:

See code changes


Legend:

  • green lines with a plus near the line numbers are newly added lines
  • red lines with a minus near the line numbers are removed lines



It looks like an instance method, it feels like an instance method, but it’s really just a static method. And instance methods do the same, behind the curtains.

Tags: , , , ,

Leave a Reply



Follow the white rabbit