替代特征

Alternative to traits

我是从 PHP 开始接触 C# 的新手,我遇到了一个问题,哪些特征是完美的,但我知道 C# 不支持特征。解决此问题的最佳方法是什么?

在 Godot 中,我想通过添加一些动画方法来让自己更轻松地制作动画,根据 PHP 我会做这样的事情。这些方法也不能真正是静态的。

public trait SpriteAnimator {

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

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

我如何在 C# 中解决这个问题?

这来自 Jordão。我不知道您在静态方法中获得了调用它的对象。

C# 没有任何与 PHP 的 trait 系统完全相似的东西,但它的某些方面可以使用 interfaces、扩展方法和偶尔的默认接口来模拟方法。

trait 最简单的用法是向 class 中添加方法,而无需 subclassing。扩展方法允许您根据对象的类型有效地向对象添加方法。只有在目标类型上公开可见的成员才能在扩展中访问。

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}.");
    }
}

静态trait数据成员更难。虽然您可以在扩展 class(上面的 DemoExtensions)中定义静态字段或 属性,但该值在运行扩展方法的所有类型之间共享。如果您需要基于调用该方法的对象类型的静态值,那么您需要在扩展 class.

中使用 Dictionary<type, ...> static 手动处理该值

另一个限制是扩展方法需要一个对象来调用:

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

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

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

注意 DoSomething 中的 this.SayHello(); 行。如果去掉 this. 部分,程序将无法编译。

默认接口方法也可以用作一种扩展,但是在访问它们之前必须将类型强制转换为接口。不再是 this.SayHello();,现在是 ((INamed)this).SayHello();,这还不是最糟糕的。他们有自己的位置,但大多数情况下对我没有用。您的里程可能会有所不同。

从 C#9 开始,除了方法之外,没有其他方法可以添加扩展类型,我非常怀疑我们在未来的任何时候都不会获得扩展属性。在这种情况发生变化之前,您必须自己想办法解决 trait 属性。