Alternative to traits

1k Views Asked by At

I'm quite new to C# coming from PHP and I have encountered a problem for which traits would be perfect but I understand that C# doesn't support traits. What is the best way to solve this?

In Godot I'd like to make the animation a bit easier on myself by adding a few methods for animation, according to PHP I'd do something like this. The methods can't really be static either.

public trait SpriteAnimator {

    public void Animate(string animation)
    {
        // DO SOMETHING
    }
    
}

public class Actor : KinematicBody2D
{
    use SpriteAnimator;
    
    public override void _Ready()
    {
        Animate("run");
    }
}

How would I solve this in C#?

1

There are 1 best solutions below

0
On BEST ANSWER

C# does not have anything that is completely analogous to PHP's trait system but some aspects of it can be emulated using interfaces, Extension Methods and occasionally Default Interface Methods.

The simplest usage of trait is to add methods to classes without subclassing. Extension methods allow you to effectively add methods to objects based on their type. Only members that are publicly visible on the targetted type will be accessible in the extension.

public interface INamed
{
    string Name { get; }
}

public static class DemoExtensions
{
    // This is available on instances of any type
    public static void SayHello(this object self)
    {
        Console.WriteLine("Hello.")
    }

    // This one will work on instances whose type implements INamed
    public static void SayHello(this INamed self)
    {
        Console.WriteLine($"Hello {self.Name}.");
    }
}

Static trait data members are more difficult. While you can define a static field or property in the extension class (DemoExtensions above) the value is shared among all types that the extension method runs on. If you need static values based on the type of object the method is called against then you'll need to handle that manually with a Dictionary<type, ...> static in the extension class.

Another limitation is that extension methods require an object to invoke:

public class MyClass : INamed
{
    public string Name { get; }

    public MyClass(string name)
    {
        Name = name;
    }

    public void DoSomething()
    {
        this.SayHello();
    }
}

Note the this.SayHello(); line in DoSomething. If you take the this. portion away the program will not compile.

Default Interface Methods can also be used as a sort of extension, but you have to force cast the type to the interface before you can access them. No more this.SayHello();, now it's ((INamed)this).SayHello(); and that's not even the worst of it. They have their place but mostly it's not useful to me. Your milage may vary.

As of C#9 there is no way to add extension types other than methods, and I very strongly doubt that we'll get Extension Properties at any point in the future. You'll have to figure your own way around trait properties until that changes.