C#如何将Lane类中两个相似判定方法重构为可复用泛型方法
解决方案
方案1:委托抽离差异化逻辑(推荐,改动最小)
直接把接口转换、判定方法调用这两处唯一的差异点通过Lambda委托传入通用方法,完全不需要修改现有接口、音符类的实现,无额外性能损耗:
public class Lane { // 提取公共逻辑的通用方法 private bool CheckKeyCommon(Func<MusicNoteGameObject, Judgement> judgeFunc, double inputBeat) { MusicNoteGameObject frontNote = visibleNotes.Peek(); Judgement j = judgeFunc(frontNote); // 此处放原来两份完全相同的剩余逻辑 } // 原按键按下方法仅保留差异化逻辑 private bool CheckKeyDown(double inputBeat) { return CheckKeyCommon(note => ((IJudgeDown)note).JudgeDown(inputBeat), inputBeat); } // 原按键抬起方法仅保留差异化逻辑 private bool CheckKeyUp(double inputBeat) { return CheckKeyCommon(note => ((IJudgeUp)note).JudgeUp(inputBeat), inputBeat); } }
方案2:泛型实现(满足泛型函数需求)
如果希望用泛型约束来规范传入的判定接口,可以用如下实现,编译期即可完成类型校验,不需要反射:
public class Lane { // 泛型通用判定方法 private bool Judge<T>(Func<T, double, Judgement> judgeMethod, double inputBeat) { MusicNoteGameObject frontNote = visibleNotes.Peek(); // 先转object再转泛型即可避免直接转换报错 T judgeObj = (T)(object)frontNote; Judgement j = judgeMethod(judgeObj, inputBeat); // 此处放原来两份完全相同的剩余逻辑 } private bool CheckKeyDown(double inputBeat) { return Judge<IJudgeDown>((obj, beat) => obj.JudgeDown(beat), inputBeat); } private bool CheckKeyUp(double inputBeat) { return Judge<IJudgeUp>((obj, beat) => obj.JudgeUp(beat), inputBeat); } }
方案3:重构接口设计(扩展性最优)
如果项目还在迭代前期,可以调整接口抽象,把判定时机作为参数传入,后续新增判定类型不需要额外新增接口:
// 判定时机枚举 public enum JudgeTiming { KeyDown, KeyUp } // 统一判定接口 public interface IJudgable { public Judgement Judge(JudgeTiming timing, double inputBeat); } // 音符类实现示例 public class TapNoteGameObject : MusicNoteGameObject, IJudgable { public Judgement Judge(JudgeTiming timing, double inputBeat) { return timing switch { JudgeTiming.KeyDown => JudgeDown(inputBeat), JudgeTiming.KeyUp => JudgeUp(inputBeat), _ => Judgement.Miss }; } // 原有JudgeDown、JudgeUp内部实现保留 } // Lane类仅需一个通用判定方法 public class Lane { private bool CheckKey(JudgeTiming timing, double inputBeat) { MusicNoteGameObject frontNote = visibleNotes.Peek(); IJudgable judgable = (IJudgable)frontNote; Judgement j = judgable.Judge(timing, inputBeat); // 公共逻辑 } }
注意事项
所有方案建议在类型转换前增加合法性校验,例如if (frontNote is IJudgeDown downObj),避免未实现对应接口的音符类型抛出强制转换异常,可根据业务逻辑处理异常场景(例如直接返回判定失败、打印错误日志等)。
内容的提问来源于stack exchange,提问作者DoraMemo
相关产品推荐
相关产品推荐

