如何让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
相关产品推荐
相关产品推荐

