Sie sind auf Seite 1von 8

alanzucconi.

com

Extension methods in C# - Alan


Zucconi
Alan Zucconi Published August 5, 2015
8-10 minutes

Following the heritage of C++, C# comes with a number


of powerful features which can either be used to massively
improve your code …or to make it completely unreadable. In
this post we’ll discuss a technique to add new methods to
already existing classes. Yes, even classes you don’t have
access to such as Vector3, Rigidbody and even string. Let’s
introduce extension methods with a practical example.

The problem

Let’s imagine you have a game object with a rigid body you
want to suddenly stop. You might want to do that for a number
of reasons, such as pausing your game or stopping the time.
A possible first approach is to set the relevant properties of
the rigid body (velocity, angular momentum, etc…) to zero, an
then set the property isKinematic to true. As a result your
game object will not be controlled by PhysX any more. This is
a possible way of doing it:

// Gets the rigidbody

public new Rigidbody2D rigidboyd2D;

public void Awake () {

rigidbody2D = GetComponent<Rigidbody2D>();

// Freezes is
public void Freeze () {

rigidbody2D.velocity = Vector2.zero;

rigidbody2D.angularVelocity = 0;

rigidbody2D.isKinematic = true;

This method works, but is not very elegant.

Introducing extension methods

It would be much more elegant if the class


Rigidbody2D itself had provided the Freezemethod. Luckily
enough, C# allows you to add a method to a class from
another file. This is done by creating a static class with has a
static method, which has a Rigidbody2D as its first parameter.
All the magic happens by adding the keyword this…

public static class Rigidbody2DExtension {

public static void Freeze (this Rigidbody2D rigidbody2D) {

rigidbody2D.velocity = 0;

rigidbody2D.angularVelocity = 0;

rigidbody2D.isKinematic = true;

By doing this, a new method called Freeze will be added to


Rigidbody2D:

rigidbody2D.Freeze();

// Equivalent to

Rigidbody2DExtension.Freeze(rigidbody2D);

Both the class and the methods can have the name you like.
If you are using Visual Studio or any other sufficiently
advanced IDE, you’ll see that Freeze is marked as an
extension method:

Handling nulls

It’s important to notice that extension methods are not


methods in the “traditional” sense. The definition of the
Rigidbody2D class has not been altered during compilation.
Quite the opposite, the compiler changes any call to
Rigidbody2D.Freeze to Rigidbody2DExtension.Freeze. This
means that even if rigidbody2D is null, the function can still be
invoked successfully. We can then embed all the integrity
check directly into the Freeze method:

public static bool Freeze (this Rigidbody2D rigidbody2D) {

if (rigidbody2D == null)

return false;

rigidbody2D.velocity = 0;

rigidbody2D.angularVelocity = 0;

rigidbody2D.isKinematic = true;

return true;

This is extremely powerful, because it allows to get rid


of many null checks. At the same time, it can hide structural
problems in your code and finding null references can be
harder.

Saving the state

Let’s improve our code by saving the state of the rigid body
so that it can be restored later:

// The state of a Rigidbody2D

public class Rigidbody2DState {

public Vector2 velocity;

public float angularVelocity;

// Inside the Rigidbody2DExtension class

public static Rigidbody2DState GetState (this Rigidbody2D


rigidbody2D) {

Rigidbody2DState state = new Rigidbody2DState();

state.velocity = rigidbody2D.velocity;

state.angularVelocity = rigidbody2D.angularVelocity;

return state;

which can be called as:

Rigidbody2DState state rigidbody2D.GetState();

Adding references

If you’re going to invoke GetState a lot, creating new objects


is not a good idea. The best way is to create the object once
and then re-use it. We can then pass the instance to
GetState so that it can be recycled.

public static Rigidbody2DState GetState (this Rigidbody2D


rigidbody2D, Rigidbody2DState state = null) {

if (state == null)

state = new Rigidbody2DState();

state.velocity = rigidbody2D.velocity;

state.angularVelocity = rigidbody2D.angularVelocity;
return state;

Which can be invoked like this:

state = rigidbody2D.GetState(state)

Other uses

C# has several interesting features which game developers


rarely encounter. Learning to master a language so powerful
can really speed up your coding. For instance, you
can extend the class string to add this interesting method:

public static bool IsNullOrEmpty(this string value) {

return string.IsNullOrEmpty(value);

// Old version

string.IsNullOrEmpty(myString);

// New version

myString.IsNullOrEmpty();

tmachineorg on Reddit suggested an extension method to get


a component, or add it if it doesn’t exist:

public static T Get<T>(this GameObject gameObject) where


T: Component {

T result = gameObject.GetComponent<T>();

if(result == null)

result = gameObject.AddComponent<T>();

return result;

Of, if you like the fancy double question mark operator:


public static T Get<T>(this GameObject gameObject) where
T: Component {

return gameObject.GetComponent<T>()
?? gameObject.AddComponent<T>();

Another interesting way of using extension method for Unity is


to simply the way the position of an object is changed. Since
transform.position is a property and not a variable, it cannot
be changed directly; trying to write into into one of its fields
(such as transform.position.x = 10) will result in an error. This
can be solve by doing this:
public static Vector3 ChangeX (this Transform transform,
float x) {

Vector3 position = transform.position;

position.x = x;

transform.position = position;

return position;

// Do the same for ChangeY and ChangeZ

// ...

// Old version

Vector3 position = transform.position;

position.x = 10;

transform.position = position;

// New version

transform.ChangeX(10);

Alternatively, you can “transform” Vector3s in packed arrays,


like the one used in shaders:

public static Vector3 xyz (this Vector3 v, float x, float y, float


z) {
v.x = x; v.y = y; v.z = z;

return v;

public static Vector3 xy (this Vector3 v, float x, float y) {

v.x = x; v.y = y;

return v;

public static Vector3 xz (this Vector3 v, float x, float z) {

v.x = x; v.z = z;

return v;

// ...

Which allows to write:


Vector3 v = Vector3.zero;

v.xz(5f,10f)

Conclusion

Extension methods are powerful enough to make your code


more expressive by creating safe shortcuts and aliases. But
all these changes are not just cosmetic; if used properly they
can dramatically reduce the number of mistakes and avoid
code duplication. The only shortage of this technique is that it
can’t be used to change an existing method. Creating
an extension method with the same signature or another one
is not an error; however, it will always be shadowed by the
original one hence, never called.

Use extension methods responsibly.

Other resources
PicosRapture: A very beefy set of method
extensions specifically designed for Unity;

PubSub from scratch: A more efficient way to implement a


messaging system in Unity without SendMessage.

Support this blog! ♥

For the past three years I've been dedicating more and more
of my time to the creation of quality tutorials, mainly about
game development and machine learning. If you think these
posts have either helped or inspired you, please consider
supporting this blog.

Das könnte Ihnen auch gefallen