2

for training purposes I am following along a Tutorial written for Java, which I successfully 'translated' into C# so far, however, I am facing now a problem for which I don't really have a clue how to solve it. The closest(?) possible answer to my problem I could find was this question. Though I have problems understanding delegates and lamba expressions for now. Anyways, here the relevant Code in Java:

public abstract class LevelUpOption {
  private String name;
  public String name() { return name; }

  public LevelUpOption(String name){
    this.name = name;
  }

  public abstract void invoke(Creature creature);
}

And another class:

public class LevelUpController {

    private static LevelUpOption[] options = new LevelUpOption[]{
        new LevelUpOption("Increased hit points"){
            public void invoke(Creature creature) { creature.gainMaxHp(); }
        },
        new LevelUpOption("Increased attack value"){
            public void invoke(Creature creature) { creature.gainAttackValue(); }
        },
        new LevelUpOption("Increased defense value"){
            public void invoke(Creature creature) { creature.gainDefenseValue(); }
        },
        new LevelUpOption("Increased vision"){
            public void invoke(Creature creature) { creature.gainVision(); }
        }
    };

    public void autoLevelUp(Creature creature){
        options[(int)(Math.random() * options.length)].invoke(creature);
    }

    public List<String> getLevelUpOptions(){
        List<String> names = new ArrayList<String>();
        for (LevelUpOption option : options){
            names.add(option.name());
        }
        return names;
    }

    public LevelUpOption getLevelUpOption(String name){
        for (LevelUpOption option : options){
            if (option.name().equals(name))
                return option;
        }
        return null;
    }
}

The problem I have is with this part:

private static LevelUpOption[] options = new LevelUpOption[]{
        new LevelUpOption("Increased hit points"){
            public void invoke(Creature creature) { creature.gainMaxHp(); }
        },
        new LevelUpOption("Increased attack value"){
            public void invoke(Creature creature) { creature.gainAttackValue(); }
        },
        new LevelUpOption("Increased defense value"){
            public void invoke(Creature creature) { creature.gainDefenseValue(); }
        },
        new LevelUpOption("Increased vision"){
            public void invoke(Creature creature) { creature.gainVision(); }
        }
    };

While easy to understand in terms what it is doing I have no clue how to write that relatively similiar in C#. I could work around it in very simplistic ways like with if or switch cases but I would like to keep it smooth to the original.

Community
  • 1
  • 1
Skyswimsky
  • 71
  • 2
  • 9
  • Well a delegate *is* the most sensible approach here - either on its own, or as part of a separate class if you really need the description part. You say you "have problems" understanding delegates - but you haven't given any details, so it's hard to help you understand them better... – Jon Skeet Jul 30 '14 at 12:45
  • I kinda understand them, at least in theory, multicasting too, I just feel very unsure about using them in praxis as I have no experience with them. – Skyswimsky Jul 30 '14 at 13:14

1 Answers1

5

There are no anonymous classes in C#, but you have two ways of achieving the same result:

  • Make private, nested, named classes, and reference them in the array initializer, or
  • Make a constructor take a delegate for each abstract method that you plan to override.

The first approach is self-explanatory, but the code would be quite a bit longer. The named classes should be OK, because they are private to the implementation of your publicly visible class.

The second approach could look as follows:

public class LevelUpOption {
  private String name;
  public String name() { return name; }

  public LevelUpOption(String name, Action<Creature> invoke){
    this.name = name;
    this.invoke = invoke;
  }

  public readonly Action<Creature> invoke;
}

Now you can initialize your array like this:

private static LevelUpOption[] options = new [] {
    new LevelUpOption("Increased hit points", c => c.gainMaxHp() ),
    new LevelUpOption("Increased attack value", c => c.gainAttackValue()),
    ...
};

Since invoke is a delegate, the syntax of calling it is the same:

options[i].invoke(myCreature);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • I think I'd put a method on the class to invoke the delegate, rather than exposing it as a public field... – Jon Skeet Jul 30 '14 at 13:17
  • That works perfect, thank you very much. Though I do not quite understand the "c => c.gainMaxHp()" part, as in, why is it "allowed" to work there? But that has more to do with me not understanding => in the first place yet. – Skyswimsky Jul 30 '14 at 13:18
  • @user3800990 `=>` is a way to construct a lambda. Lambda to a delegate in C# is roughly the same as an anonymous class to its base class in Java. The difference is that in Java an anonymous class can implement as many methods as it wishes, while a lambda can "implement" only one method that matches the signature of its delegate. – Sergey Kalinichenko Jul 30 '14 at 13:25