【问题标题】:How to boil down similar looking C# methods into a generic one如何将外观相似的 C# 方法归结为通用方法
【发布时间】:2021-11-13 00:39:08
【问题描述】:

是否可以将Lane 中的两个方法归结为一个泛型函数,可以通过传入接口和方法名称来重用?还是有更好的方法来解决这个问题?


上下文:我正在做一个节奏游戏,其中一个音符是通过key down和key up来判断的,目前我有以下接口和方法:

public interface IJudgeDown {
    public Judgement JudgeDown(double inputBeat);
}

public interface IJudgeUp {
    public Judgement JudgeUp(double inputBeat);
}
public class Lane {
    private bool CheckKeyDown(double inputBeat) {

        MusicNoteGameObject frontNote = visibleNotes.Peek();
        IJudgeDown downObj = (IJudgeDown)frontNote;
        Judgement j = downObj.JudgeDown(inputBeat);
        // ... (identical to the rest of CheckKeyUp)
    }

    private bool CheckKeyUp(double inputBeat) {

        MusicNoteGameObject frontNote = visibleNotes.Peek();
        IJudgeUp upObj = (IJudgeUp)frontNote;
        Judgement j = upObj.JudgeUp(inputBeat);
        // ... (identical to the rest of CheckKeyDown)
    }
}

MusicNoteGameObject 本身并没有实现这两个接口。但是,此基类针对实现了这两个接口的不同笔记类型进行了扩展(例如,TapNoteGameObject : MusicNoteGameObject, IJudgeDown, IJudgeUp)。


初次尝试失败:

public interface IJudge { }   // Have an empty interface
public interface IJudgeDown : IJudge { ... }
public interface IJudgeUp : IJudge { ... }

private bool Judge<I>(Func<double, Judgement> method, double inputBeat) where I : IJudge {

    MusicNoteGameObject frontNote = visibleNotes.Peek();
    I obj = (I)frontNote;   // This cast is invalid
    Judgement j = obj.method(inputBeat);   // How should I call the method on obj? Reflection?
    // ...
}

要将正确的方法传递给Judge&lt;&gt;,我必须将frontNote 投射到Judge&lt;&gt; 之外,如下所示:

MusicNoteGameObject frontNote = visibleNotes.Peek();
IJudgeDown downObj = (IJudgeDown)frontNote;
Judge<IJudgeDown>(downObj.JudgeDown, inputBeat);

但是我不需要再传入IJudgeDown,我可以将Judge&lt;&gt; 的其余部分包装到一个函数中并调用它,从而不需要Judge&lt;&gt; 方法...

【问题讨论】:

    标签: c# generics inheritance delegates


    【解决方案1】:

    您可以像这样合并成一个方法:

    public enum JudgeAction { Up, Down }
    private bool CheckKey(JudgeAction action, double inputBeat) 
    {
        var frontNote = visibleNotes.Peek();
        
        switch (action)
        {
           case JudgeAction.Down:
             var downObj = frontNote as IJudgeDown;
             downObj?.JudgeDown(inputBeat);
           break;
           case JudgeAction.Up:
             var upObj = frontNote as IJudgeUp;
             upObj?.JudgeUp(inputBeat);
           break;
        }
        // rest of your code
    }
    

    【讨论】:

    • 似乎有一个误解:你的答案连接了两个操作,JudgeDownJudgeUp,但我想要一种“主”方法来替换 CheckKeyDownCheckKeyUp允许从它派生这两种方法。例如,CheckKey&lt;IJudgeDown&gt; 将与当前的 CheckKeyDown 相同。
    • 根据您的反馈进行了更新。使用泛型类型和接口层次结构会使事情复杂化。鉴于您的 frontNote 实现了这两个接口,并且您需要以某种方式指定类型的 JudgeAction,我选择使用实现此目标的枚举,而不会使您的类型和方法签名进一步复杂化。
    • 使用 switch case 似乎是复杂性和便利性之间的良好折衷。我想要一个通用的CheckKey函数的原因是因为当更多类型的Judge...s被实现时,我不想在其他地方手动添加代码来方便它们。换句话说,无需修改CheckKey...中的任何内容就能够编写一个新的接口及其唯一的方法是我最终想要的,但感谢您的建议!
    猜你喜欢
    • 2023-03-14
    • 2021-07-16
    • 1970-01-01
    • 2017-12-17
    • 2021-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多