2

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#?

K1ll3rM
  • 169
  • 8
  • 1
    Take a look at extension methods, use them with interfaces. Related: https://stackoverflow.com/questions/6644668/mixins-with-c-sharp-4-0 – Jordão Oct 04 '20 at 12:49
  • @Jordão As far as I've seen those can only be static which kind of defeats the purpose of this. – K1ll3rM Oct 04 '20 at 12:51
  • Nevermind, I thought you could only do static but they work slightly differently than normal. Thank you @Jordão – K1ll3rM Oct 04 '20 at 12:59

1 Answers1

0

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.

Corey
  • 15,524
  • 2
  • 35
  • 68