public static void Draw(this Bounds bounds, Color color)

Hello,

I’m looking through someone else’s code base on a small project, something I’ve not really done before to any extent. There’s a few things in the code base I’m not used to seeing, and one such thing is copious use of “this”, including in places where I know it isn’t strictly necessary. However, one place they use “this” is in the parameters of functions in a public static class called “Extensions”, e.g:

public static Vector3 ToVector3(this Vector3Int vector) {
        return (Vector3)(vector);
    }

    public static T PickRandom<T>(this ICollection<T> collection) {
        int index = UnityEngine.Random.Range(0, collection.Count);
        return collection.ElementAt(index);
    }

public static T GetBest<T>(this IEnumerable<T> enumerable, Func<T, float> property) {
        float bestValue = float.NegativeInfinity;
        T bestItem = default(T);
        foreach (var item in enumerable) {
            float value = property.Invoke(item);
            if (value > bestValue) {
                bestValue = value;
                bestItem = item;
            }
        }
        return bestItem;
    }

Is there any reason to use “this” in front of the first parameter like in the code above?

To give a random example of how else they’re using “this”, here’s the top of one of their scripts:

public class InfiniteMap : AbstractMap {
    private Dictionary<Vector3Int, Slot> slots;

    public readonly int Height;

    public Vector3Int rangeLimitCenter;
    public int RangeLimit = 80;

    private TilingMap defaultColumn;

    public InfiniteMap(int height) : base() {
        this.Height = height;
        this.slots = new Dictionary<Vector3Int, Slot>();
        this.defaultColumn = new TilingMap(new Vector3Int(1, height, 1));

        if (ModuleData.Current == null || ModuleData.Current.Length == 0) {
            throw new InvalidOperationException("Module data was not available, please create module data first.");
        }
    }

As far as I can see, “this” is entirely superfluous in the “InfiniteMap” function, but I’m also not sure about why this function has the " : base()" on the end (I think it’s just calling the constructor for the base class, but I’m not sure).

That is known as an extension method in C#. Pretty handy feature to cut down code noise:

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods

this. is superfluous until you have a local variable (or argument) named the same. Personally I don’t like identically-named variables and will go out of my way to name my arguments differently than class instance variables, but a lot of folks like to always put in this. to signal that it is a member variable.

That’s it, and one reason is to allow different arguments to a base constructor than the derived constructor, which might even have more (or fewer) constructor variants.

Also, by the way, this:

… is an awesome way to expand your brain. You should expose your brain to as much code as possible, as every way might reveal to you a new and interesting approach to problem solving. It’s one of the main benefits I get from reading these forums, learning about Unity (and C# and gamedev in general) features I had never heard of before.

3 Likes

As mentioned, in the first example you’ve posted, the “this” keyword is used to denote an extension method, which is a very cool feature in C#, in my opinion.

Extension methods allow you to add new methods to types outside of their definition file, which is especially handy for built-in libraries such as Unity’s, where you can’t, say, open/edit the Vector3 definition & add a new method.
You can then call that method like you normally would from any instance of an object.

For example, say you want to constrain a number to a minimum value. You could do that usually in a few ways such as:

int myNum = 10;

int minValue1 = Mathf.Clamp(myNum, 20, int.MaxValue);
int minValue2 = myNum < 20 ? 20 : myNum;

Constraining values can be a pretty frequent requirement though, and having to type one of the above methods every time can feel pretty verbose & repetitive.
You obviously can open up the int definition and add a new method to simplify the process, so that’s where extension methods come in.

You can define a static class (extension methods must be static) with your method like so:

public static class IntExtensions
{
  //The "this" keyword here is the important part - it denotes that this method will belong to an instance of the "int" type.
  //The "value" parameter is the int instance being passed automatically to this method.
  public static int Min(this int value, int minValue)
  {
    return value < minValue ? minValue : value;
  }
}

And now you can use your new method anywhere like so:

int myNum = 10;

int minValue = myNum.Min(20);
1 Like

One exception: constructors. If you need to pass something into a constructor that is going to go into the field X, then the constructor parameter absolutely should be named X, with the constructor having the line this.X = X;

Is that what’s happening in the following code?

public InfiniteMap(int height) : base() {
        this.Height = height;
        this.slots = new Dictionary<Vector3Int, Slot>();
        this.defaultColumn = new TilingMap(new Vector3Int(1, height, 1));
        if (ModuleData.Current == null || ModuleData.Current.Length == 0) {
            throw new InvalidOperationException("Module data was not available, please create module data first.");
        }

Here, it looks to me like the int “height” is being passed into the “InfiniteMap()” constructor, and that int “height” seems to get passed into a field “Height”. But the parameter “height” is lower-case and the field “Height” starts with an upper case “H”…

Yes, that’s what’s happening.

For clarity, when I say “absolutely should be named X”, this isn’t a functionality thing - it’s just so that when I see the constructor, I know where my data is going to end up. So it being a different case doesn’t matter functionally.

I’ve had a look at this: Extension Methods - C# | Microsoft Learn

There’s something I’m a bit confused about. Here’s the top of the Extensions.cs file I have in the project I’m looking at:

using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System;

public static class Extensions
{
    //Extension methods are defined as static methods but are called by using instance method syntax. Their first parameter specifies which type the method operates on
    public static void Draw(this Bounds bounds, Color color) {
        var e = bounds.extents;
        Debug.DrawLine(bounds.center + new Vector3(+e.x, +e.y, +e.z), bounds.center + new Vector3(-e.x, +e.y, +e.z), color);
        Debug.DrawLine(bounds.center + new Vector3(+e.x, -e.y, +e.z), bounds.center + new Vector3(-e.x, -e.y, +e.z), color);
        Debug.DrawLine(bounds.center + new Vector3(+e.x, -e.y, -e.z), bounds.center + new Vector3(-e.x, -e.y, -e.z), color);
        Debug.DrawLine(bounds.center + new Vector3(+e.x, +e.y, -e.z), bounds.center + new Vector3(-e.x, +e.y, -e.z), color);

        Debug.DrawLine(bounds.center + new Vector3(+e.x, +e.y, +e.z), bounds.center + new Vector3(+e.x, -e.y, +e.z), color);
        Debug.DrawLine(bounds.center + new Vector3(-e.x, +e.y, +e.z), bounds.center + new Vector3(-e.x, -e.y, +e.z), color);
        Debug.DrawLine(bounds.center + new Vector3(-e.x, +e.y, -e.z), bounds.center + new Vector3(-e.x, -e.y, -e.z), color);
        Debug.DrawLine(bounds.center + new Vector3(+e.x, +e.y, -e.z), bounds.center + new Vector3(+e.x, -e.y, -e.z), color);

        Debug.DrawLine(bounds.center + new Vector3(+e.x, +e.y, +e.z), bounds.center + new Vector3(+e.x, +e.y, -e.z), color);
        Debug.DrawLine(bounds.center + new Vector3(+e.x, -e.y, +e.z), bounds.center + new Vector3(+e.x, -e.y, -e.z), color);
        Debug.DrawLine(bounds.center + new Vector3(-e.x, +e.y, +e.z), bounds.center + new Vector3(-e.x, +e.y, -e.z), color);
        Debug.DrawLine(bounds.center + new Vector3(-e.x, -e.y, +e.z), bounds.center + new Vector3(-e.x, -e.y, -e.z), color);
    }
}

“Draw()” is then used in another script, “Portal.cs”, and the top of Portal.cs looks like this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Portal {

    public void Draw(Color color) {
        this.Bounds.Draw(color);
    }

}

I’m wondering why “this.Bounds.Draw” is in scope in Portal.cs when there’s no mention of “Extensions” in the access modifiers. This might have something to do with another thing I’ve vaguely remembered about static classes on one script being accessible from other scripts in Unity, but what’s going on here?

I’m thinking that just works to make C# extensions in general useful. If I had to get to the technical bits, it might be because the namespace where Portal lives ALSO has a class (called Extensions, who cares, could be anything) that defines an extension method called Draw() for Bounds.

I think that is what gets you the extension magic. Remember, it’s just a weird sugary way of writing a bunch of otherwise legit code. It just writes the same code you would do if you wrote it as a static.

The file containing the extension methods just simply needs to exist in the same C# project that you want to use them in (or it could be added via project referencing, but that’s beyond the topic).

If the file containing the extension methods is within a namespace, you still need to import that namespace wherever you want to use those merhods, otherwise the methods are available globally by default.

2 Likes

I’d just like to clarify some things:

Extension methods do not really “add” methods to the other class, it just appears that way. The “this” keyword at the first argument indicates the compiler that this is an extension method for that type of that first argument. This is all just “syntactic sugar” / compiler magic that simplifies the usage. When you do

Bounds.Draw(color);

What the compiler actually does is this:

Extensions.Draw(Bounds, color);

Some more details

Fun fact:
actually every instance method, even those defined inside the class itself fundamentally behave the same way. The code for a method is not really part of the object instance. All instance methods always have an implicit “this” argument. However it’s actually hidden behind the OOP syntax. The executable code of the method is actually static code. If you have two instances of the same class, both classes use the same static code for their instance methods. When you call that method on instance A you implicitly pass that object instance as first parameter to the method. That’s where the context / scope comes from inside the instance method.

The scripting language LUA is a fantastic example that shows this principle. It does not really have some special OOP stuff, but it has a special member access operator besides the dot. When you use a colon inside of a dot in LUA you get exactly this OOP behaviour. I don’t want to dive too much into LUA as it has nothing to do with Unity ^^ Though here’s just a small example

-- this is a comment

local function printNameAndNumber(self, number)
    print(self.name, " = ", number)
end

local myTable = { } --this creates a new empty "table"
myTable.printNameAndNumber = printNameAndNumber;
myTable.name = "Bob"

local secondTable = { }
secondTable.printNameAndNumber = printNameAndNumber;
secondTable.name = "Alice"

Here we created two tables and each table has two members: “printNameAndNumber” and “name”. “printNameAndNumber” in each table is just a reference to the same method so the method does not even know that the table exists. We can call this method like this

myTable.printNameAndNumber(myTable, 42)

As you can see we pass myTable into the method as the first argument so the method can actually read the name field of the table. Instead we can also use the colon operator like this:

myTable:printNameAndNumber(42)

This does exactly the same thing. Now it implicitly passes the table we use the colon operator on as the first argument so it can be used inside the method.

In LUA you still can use the method normally and even pass a completely different table as the first argument. In C# this little implementation detail is completely hidden from you, but it works similar. When you use reflection in C# you can actually get a glimpse about that fact when you look at the Invoke method of the MethodInfo class. It also has a seperate object reference where you have to pass in the actual object you want to call this method on. The MethodInfo is essentially a wrapper for the static method itself that does not belong to a particular instance. The instance is always passed as first argument.

Delegates in C# essentially consist of a MethodInfo and an object reference (which would be null for actual static methods). The CreateDelegate method even calls this object reference “firstArgument”

2 Likes

Well, I prefer in general to prefix my arguments with an “a” (for argument). Though that’s my personal thing. It generally helps to identify the kind of variable and doesn’t clash with other local variables or member variables.

public MyConstructor(int aHeight)
{
    height = aHeight;
}

I totally agree that when you define arguments in the constructor that it should conceptionally be named the same. So calling the argument “level” and the field “tier” would be bad. Consistency when naming concepts is important. Otherwise you will get lost when you come back to that code in 2 years. I’ve seen code where they had like 4 different naming variations for the same value within the same class. That’s a recipe for disaster :slight_smile: