Table of Contents

Casting

Casting can be done in many ways, and is the only method to box and unbox types to/from their base definition. Casting only works for types that inherit the base type that you want to unbox from. IUser cannot be cast to IMessage.

Note

Interfaces can be cast to other interfaces, as long as they inherit each other. The same goes for reverse casting. As long as some entity can be simplified into what it inherits, your cast will pass.

Boxing

A boxed object is the definition of an object that was simplified (or trimmed) by incoming traffic, but still owns the data of the originally constructed type. Boxing is an implicit operation.

Through casting, we can unbox this type, and access the properties that were inaccessible before.

Unboxing

Unboxing is the most direct way to access the real definition of an object. If we want to return a type from its interface, we can unbox it directly.

IUser user;

// Here we use inline unboxing to make a call to its member (if available) only once.

// Note that if the entity we're trying to cast to is null, this will throw a NullReferenceException.
Console.WriteLine(((IGuildUser)user).Nickname);

// In case you are certain the entity IS said member, you can also use unboxing to declare variables.
IGuildUser guildUser = (IGuildUser)user;

Regular casting

In 'regular' casting, we use the as keyword to assign the given type to the object. If the boxed type can indeed be cast into given type, it will become said type, and its properties can be accessed.

// Say we have an entity; for the simplicity of this example, it will appear from thin air.
IChannel channel;

// If we want this to be an ITextChannel so we can access the properties of a text channel inside of a guild, an approach would be:
ITextChannel textChannel = channel as ITextChannel;

await textChannel.DoSomethingICantWithIChannelAsync();
Warning

If the type you're casting to is null, a NullReferenceException will be thrown when it's called. This makes safety casting much more interesting to use, as it prevents this exception from being thrown.

Safety casting

Safety casting makes sure that the type you're trying to cast to can never be null, as it passes checks upon calling them.

There are 3 different ways to safety cast an object:

Basic safety casting:

To safety cast an object, all we need to do is check if it is of the member type in a statement. If this check fails, it will continue below, making sure we don't try to access null.

IUser user;

// Here we check if the user is an IGuildUser, if not, let it pass. This ensures its not null.
if (user is IGuildUser)
{
    Console.WriteLine("This user is in a guild!");
}
// Check failed.

Object declaration:

Here we declare the object we are casting to, making it so that you can immediately work with its properties without reassigning through regular casting.

IUser user;

// Here we can pre-define the actual declaration of said IGuildUser object,
// so we don't need to cast additionally inside of the statement.
if (user is IGuildUser guildUser)
{
    Console.WriteLine(guildUser.JoinedAt);
}
// Check failed.

Reverse passage:

In previous examples, we want to let code continue running after the check, or if the check fails. In this example, the cast will return the entire method (ignoring the latter) upon failure, and declare the variable for further use into the method:

private void MyFunction(IMessage message)
{
    // Here we do the reverse as in the previous examples, and let it continue the code below if it IS an IUserMessage
    if (message is not IUserMessage userMessage)
        return;

    // Because we do the above check inline (don't give the statement a body),
    // the code will still declare `userMessage` as available outside of the above statement.
    Console.WriteLine(userMessage.Author);
}
Note

Usage of is, not and as is required in cast assignment and/or type checks. ==, != and = are invalid assignment, as these operators only apply to initialized objects and not their types.