4
    Character[] PlayerOne = new Character[5];

    PlayerOne[1] = new BladeWarrior();
    PlayerOne[2] = new FistWarrior();
    PlayerOne[3] = new Archer();
    PlayerOne[4] = new RedMage();
    PlayerOne[5] = new BlueMage();  

I know through polymorphism, a BladeWarrior can be a Character but it cant be the other way around. My problem is that when I try to access an element of an array. Player[1] for example, I cant access functions and variables from the BladeWarrior class. It's only letting me access variables and functions from the Character class which the BladeWarrior class inherits from.

I'm going to need to be able to access Bladewarrior functions/variables if Im going to want 2 characters to fight.

I was thinking I could use the "as" function to set PlayerOne[1] as the specific object . Not exactly like this:

string s = objArray[i] as string;

The line of code above is just to get an idea of which "as" Im talking about.

What is a solution to this problem?

1
  • You can have individual lists too based on the parent list, like PlayerOne.OfType<BladeWarrior>().ToArray(), and similarly others. Commented Dec 25, 2013 at 15:32

6 Answers 6

2

I'm going to need to be able to access Bladewarrior functions/variables if Im going to want 2 characters to fight.

It looks like you are attempting to do a multiple dispatch: you want the call of

Fight(PlayerOne[i], PlayerOne[j]);

to call a function that knows the exact types of both characters. There are different tricks that you can use to achieve double dispatch in single dispatch languages, most notably, the visitor pattern. Starting with C#4, you could also use dynamic to implement double dispatch in a relatively clean and easy to read way:

static class FightArena {
    public static void Fight(dynamic a, dynamic b) {
        try {
            DoFight(a, b);
        } catch {
            Console.Error.WriteLine("{0} and {1} cannot fight", a, b);
        }
    }
    private static void DoFight(BladeWarrior a, Archer b) {
    }
    private static void DoFight(BladeWarrior a, FistWarrior b) {
    }
    private static void DoFight(BladeWarrior a, RedMage b) {
    }
    private static void DoFight(BladeWarrior a, BlueMage b) {
    }
    private static void DoFight(BladeWarrior a, BladeWarrior b) {
    }
    private static void DoFight(Archer a, Archer b) {
    }
    ... // Enumerate all pairs that can fight
}

Now you can write something like this:

FightArena.Fight(PlayerOne[i], PlayerOne[j]);

and the call will reach the exact pair of types based on dynamic types of PlayerOne[i] and PlayerOne[j].

Sign up to request clarification or add additional context in comments.

2 Comments

So essentially, based on the arguments in Fight(dynamic a, dynamic b) We let the program know, "hey we need 2 arguments, but they can be dynamic". We pass the 2 main Characters through that argument, then within that function we initiate the fight. Because we have multiple DoFight functions, the program will then pick up on which dynamic argument was thrown into the parameters then use the corresponding DoFight function?
@user3134679 Yes, that's what is going to happen. Since both a and b are declared dynamic, the runtime type will be considered when selecting the proper override of the DoFight method.
1

If you have to cast your Character's back to their concrete types for them to fight you are loosing any benefit you would get from polymorphism. If you need to cast to a Character to a Bladewarrior to fight that means you have to write different logic to each character to be able to fight each other. Then if you later add a new character type you'll have to update all of your fight code to support that type.

What you really want to be able to do is to write a generate fight algorithm that can be used to fight any two Character objects without casting them. You could add properties to the Character base class that would indicate the properties that the character has.

For example you could have add a Attacks property to the Character class which would be a list of the attacks the character has which would have a name of the attack and how much damage it does. Then each derived character class would populate its list of Attacks and your fight algorithm would process these attacks. In this way each character could have its own list of attacks, but would not have to be cast to a specific character type to access it.

The end goal here is for any external code to only know about the Character class and not any of its derived classes. That will make your external code cleaner and give you the ability to add or remove character classes in the future.

Comments

0

It is absolutely normal,if you want access BladeWarrior's properties you must convert your Character to BladeWarrior, and you'r right you can use as keyword for this:

BladeWarrior bw = PlayerOne[1] as BladeWarrior;

1 Comment

Thank you. Ive added that line of code under what I have made already.Im able to access the functions/variables by doing : bw.normalattack(); but I cant access it by doing: PlayerOne[1].normalattack();
0

You are upcasting the instances thats why you are only able to use parent class behaviours and attributes.

I think you will have to do i individually for all instances.

BladeWarrior player1 = new BladeWarrior();
FistWarrior player2 = new FistWarrior();
Archer player3 = new Archer();
//and so on

3 Comments

The reason for me using an array was because when it came to intiating the fight instance, I could pass the array as an argument into a fight option then in that function determine what type of character is being attacked/dealing damage. Is there a better approach? Maybe using override functions, 1 for each type of character maybe? maybe like: public void normal attack(ref Bladewarrior BW) public void normal attack(ref Archer arch) and let the program determine which function is going ot be used?
@user3134679 I think dasblinkenlight answer is the better solution in your case.
Does his answer still make use of polymorphism?
0

As you said you can use "as" to cast. If the instance you are trying to cast cannot be casted to the target class you will get an null.

var currentCharacter = PlayerOne[1] as BladeWarrior;
if(currentCharacter  != null)
{
....
}

The challenge here is to get a clean way to know what is the right casting. May be you could use a structure in the array and use a flag to indicate the underliying class

switch(PlayerOne[1].Type)
{
    case PlayerTypes.BladeWarrior:
    currentCharacter = PlayerOne[1].Character as BladeWarrior;
}

But in general it seems you are not acomplishing the Liskov Subtitution Principle (The L in SOLID principles). You shouldnt need to access to the implementation details of the specific types of characters, Just override some methods or use a more modular(and complex) design based on the strategy pattern.

Comments

0

or you can use interface

public interface ICharacter {
    int myValue { get; set;}
    void myMethod();
}

public class BladeWarrior : ICharacter {
    private int myPrivateValue;
    public int myValue { get { return myPrivateValue; } set { myPrivateValue = value; } }

    public void myMethod() {
    //Do what you want
    }
}


ICharacter[] PlayerOne = new ICharacter[5];
PlayerOne[0] = new BladeWarrior();

then you can access your interface methods ICharacter[0].myMethod();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.