简体   繁体   中英

How to access properties from an array of class objects in C#?

    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?

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] .

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.

这是绝对正常的,如果您想要访问BladeWarrior的属性,您必须将您的Character转换为BladeWarrior ,而您可以将其as关键字:

BladeWarrior bw = PlayerOne[1] as BladeWarrior;

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

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.

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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM