You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让C#类方法复用相同逻辑以支持float和Vector2类型?

嘿,这个重复代码的问题我太熟了!你的核心诉求是让相同的运动逻辑适配不同数值类型(float/Vector2),又不想写两遍几乎一样的代码——C#其实有几种优雅的解决方式,我给你拆解一下:

最佳方案:用C# 11+的静态抽象接口(强类型+无重复)

如果你用的是C# 11或更高版本(比如Unity 2022.3+已经支持),静态抽象接口成员就是专门解决这种“泛型需要运算符支持”的问题的。它允许我们在接口里定义静态的运算符方法,让目标类型实现这些接口后,泛型就能直接用运算逻辑。

第一步:定义运算接口

先写一个接口,把Move方法里用到的所有运算都列出来——减法、加法、和float的乘法:

public interface IMotionCalculable<T>
{
    static abstract T operator -(T left, T right);
    static abstract T operator +(T left, T right);
    static abstract T operator *(T value, float scalar);
}

第二步:给目标类型做“适配包装”

因为原生的float和Unity的Vector2我们没法直接修改加接口,所以写个轻量的包装结构体,同时加隐式转换,用起来和原生类型一样丝滑:

针对float的包装:

public struct FloatMotion : IMotionCalculable<FloatMotion>
{
    public float Value { get; }

    public FloatMotion(float value) => Value = value;

    public static FloatMotion operator -(FloatMotion left, FloatMotion right)
        => new FloatMotion(left.Value - right.Value);

    public static FloatMotion operator +(FloatMotion left, FloatMotion right)
        => new FloatMotion(left.Value + right.Value);

    public static FloatMotion operator *(FloatMotion value, float scalar)
        => new FloatMotion(value.Value * scalar);

    // 隐式转换,直接和float互转不用手动拆包
    public static implicit operator FloatMotion(float value) => new FloatMotion(value);
    public static implicit operator float(FloatMotion motion) => motion.Value;
}

针对Vector2的包装:

public struct Vector2Motion : IMotionCalculable<Vector2Motion>
{
    public Vector2 Value { get; }

    public Vector2Motion(Vector2 value) => Value = value;

    public static Vector2Motion operator -(Vector2Motion left, Vector2Motion right)
        => new Vector2Motion(left.Value - right.Value);

    public static Vector2Motion operator +(Vector2Motion left, Vector2Motion right)
        => new Vector2Motion(left.Value + right.Value);

    public static Vector2Motion operator *(Vector2Motion value, float scalar)
        => new Vector2Motion(value.Value * scalar);

    // 同样加隐式转换,和Vector2无缝切换
    public static implicit operator Vector2Motion(Vector2 value) => new Vector2Motion(value);
    public static implicit operator Vector2(Vector2Motion motion) => motion.Value;
}

第三步:重构你的控制器为泛型类

现在把原来的类改成泛型,只需要写一遍Move逻辑:

public class MotionController<T> where T : IMotionCalculable<T>
{
    public float Response { get; } = 0.2f;
    public float Damping { get; } = 0.5f;
    public T Velocity { get; set; }

    public T Move(T current, T target)
    {
        T delta = target - current;
        Velocity += delta * Response;
        current += Velocity;
        Velocity *= Damping;
        return current;
    }
}

用起来超简单:

// Float版本的控制器
var floatController = new MotionController<FloatMotion>();
float currentFloat = 1f;
currentFloat = floatController.Move(currentFloat, 5f);

// Vector2版本的控制器
var vectorController = new MotionController<Vector2Motion>();
Vector2 currentVec = new Vector2(1,1);
currentVec = vectorController.Move(currentVec, new Vector2(5,5));

兼容旧版本C#的方案:委托驱动的泛型

如果你还在用C# 11之前的版本(比如老Unity项目),没法用静态抽象接口,那就把运算逻辑做成委托,注入到泛型类里——虽然有点繁琐,但胜在兼容:

public class MotionController<T>
{
    public float Response { get; } = 0.2f;
    public float Damping { get; } = 0.5f;
    public T Velocity { get; set; }

    private readonly Func<T, T, T> _subtract;
    private readonly Func<T, T, T> _add;
    private readonly Func<T, float, T> _multiplyByScalar;

    // 构造函数里传入对应类型的运算逻辑
    public MotionController(Func<T, T, T> subtract, Func<T, T, T> add, Func<T, float, T> multiplyByScalar)
    {
        _subtract = subtract;
        _add = add;
        _multiplyByScalar = multiplyByScalar;
    }

    public T Move(T current, T target)
    {
        T delta = _subtract(target, current);
        Velocity = _add(Velocity, _multiplyByScalar(delta, Response));
        current = _add(current, Velocity);
        Velocity = _multiplyByScalar(Velocity, Damping);
        return current;
    }
}

使用示例:

// 初始化float版本的控制器
var floatController = new MotionController<float>(
    (a, b) => a - b,
    (a, b) => a + b,
    (val, scalar) => val * scalar
);

// 初始化Vector2版本的控制器
var vectorController = new MotionController<Vector2>(
    (a, b) => a - b,
    (a, b) => a + b,
    (val, scalar) => val * scalar
);

偷懒小技巧:不用包装类的模式匹配(适合类型少的场景)

如果不想写包装类或委托,也可以用模式匹配直接在方法里处理不同类型——虽然类型安全稍弱,但代码最简洁,适合你目前只有float和Vector2的情况:

public class MotionController
{
    public float Response { get; } = 0.2f;
    public float Damping { get; } = 0.5f;
    public object Velocity { get; set; }

    public T Move<T>(T current, T target)
    {
        switch (current)
        {
            case float fCurrent:
                float fTarget = (float)(object)target;
                float fVelocity = (float)Velocity;
                float delta = fTarget - fCurrent;
                fVelocity += delta * Response;
                fCurrent += fVelocity;
                fVelocity *= Damping;
                Velocity = fVelocity;
                return (T)(object)fCurrent;
            case Vector2 vecCurrent:
                Vector2 vecTarget = (Vector2)(object)target;
                Vector2 vecVelocity = (Vector2)Velocity;
                Vector2 deltaVec = vecTarget - vecCurrent;
                vecVelocity += deltaVec * Response;
                vecCurrent += vecVelocity;
                vecVelocity *= Damping;
                Velocity = vecVelocity;
                return (T)(object)vecCurrent;
            default:
                throw new NotSupportedException($"类型 {typeof(T)} 暂时不支持");
        }
    }
}

这种方式不用额外的类,以后要加新类型直接在switch里加分支就行。


内容的提问来源于stack exchange,提问作者niksga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:07:22