Reputation: 53
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?
Upvotes: 4
Views: 948
Reputation: 765
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();
Upvotes: 0
Reputation: 405
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.
Upvotes: 0
Reputation: 31394
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.
Upvotes: 1
Reputation: 726479
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]
.
Upvotes: 2
Reputation: 12439
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
Upvotes: 0
Reputation: 101681
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;
Upvotes: 0