Laden...

[gelöst]Klassenobjekt an generische Methode übergeben...aber wie?

Erstellt von CompilerSaysNo vor 3 Jahren Letzter Beitrag vor 3 Jahren 829 Views
C
CompilerSaysNo Themenstarter:in
7 Beiträge seit 2020
vor 3 Jahren
[gelöst]Klassenobjekt an generische Methode übergeben...aber wie?

Hallo liebe Community,

bei der Übergabe eines Klassenobjektes an eine generische Methode will bei mir irgendwas nicht funktionieren und ich komm einfach nicht dahinter. Hab schon wie wild gesucht, aber finde keine Lösung oder checke es nicht. Ich hoffe ihr könnt mir helfen.

Ich möchte ein Objekt der folgenden Klasse...


public class NPC : Human
{
        public string NPCFunction { get; set; }
        public int FirstHandID { get; set; }
        public int SecondHandID { get; set; }
        public int ArmorID { get; set; }

        public NPC() { }
        public NPC(int humanID, string humanName, byte humanAge, short humanLifePoints, short humanAttack, short humanDefense, string npcFunction, int firstHandID, int secondHandID, int armorID)
            : base(humanID, humanName, humanAge, humanLifePoints, humanAttack, humanDefense)
        {
            FirstHandID = firstHandID;
            SecondHandID = secondHandID;
            ArmorID = armorID;
            FirstHandID = firstHandID;
            NPCFunction = npcFunction;
        }
}  

...an folgende Methode übergeben:


public static Weapon ReadDataCWeapon(NPC npc)
{
        string weaponsFile = "weapons.csv";
        string[] lines = File.ReadAllLines(weaponsFile);
            
        Weapon activeWeapon = null;
        for (int i = 0; i < lines.Length; i++)
        {
            string[] parts = lines[i].Split(";");
            if (npc.FirstHandID == Convert.ToInt32(parts[0]))
            {
                activeWeapon = new Weapon(Convert.ToInt32(parts[0]), parts[1], Convert.ToInt16(parts[2]), parts[3], parts[4], Convert.ToInt16(parts[5]));
            }
        }
        return activeWeapon;
}

Dies war meine bisherige Lösung und funktioniert einwandfrei.

Da ich Objekte des Typs NPC, sowie des Typs PC an diese Methode übergeben muss, hatte ich dies bisher mit Funktionsüberladung gelöst und zwei identische Methoden, die sich lediglich beim Übergabeparameter unterscheiden.

Nun wollte ich dies mit einer generischen Methode, wie folgt, lösen:


public static Weapon ReadDataCWeapon<T>(T c)
{
            string weaponsFile = "weapons.csv";
            string[] lines = File.ReadAllLines(weaponsFile);
            
            Weapon activeWeapon = null;
            for (int i = 0; i < lines.Length; i++)
            {
                string[] parts = lines[i].Split(";");
                if (c.FirstHandID == Convert.ToInt32(parts[0]))
                {
                    activeWeapon = new Weapon(Convert.ToInt32(parts[0]), parts[1], Convert.ToInt16(parts[2]), parts[3], parts[4], Convert.ToInt16(parts[5]));
                }
            }
            return activeWeapon;
}

Beim Aufruf der Methode scheint dies auch kein Problem zu verursachen:


Weapon activeWeaponFighter2 = IOClass.ReadDataCWeapon<NPC>(fighter2);

Allerdings bekomme ich bei der generischen Methode in folgender Zeile...


if (c.FirstHandID == Convert.ToInt32(parts[0]))

folgenden Fehler:

Fehlermeldung:
Fehler CS1061 "T" enthält keine Definition für "FirstHandID", und es konnte keine zugängliche FirstHandID-Erweiterungsmethode gefunden werden, die ein erstes Argument vom Typ "T" akzeptiert (möglicherweise fehlt eine using-Direktive oder ein Assemblyverweis). MiniRPG C:... 45 Aktiv

So wie es aussieht erkennt die Methode nicht, dass es sich um ein Objekt der Klasse NPC handelt. Warum ist das so?

Gruß
Maik

2.080 Beiträge seit 2012
vor 3 Jahren

Das ist so, weil der Typ beim Kompilieren noch nicht bekannt ist, Du kannst ja theoretisch alles übergeben, sogar eine Zahl.
Du kannst das Problem mit Constraints beheben, z.B. indem Du eine Bedingung für den generischen Parameter setzt.

Beispiel mit einem Interface:

public interface IHasHands
{
    public int FirstHandID { get; set; }
    public int SecondHandID { get; set; }
}

public class NonPlayerCharacter : Human, IHasHands
{
    // ...
}

public class PlayerCharacter : Human, IHasHands
{
    // ...
}

public static Weapon ReadDataCWeapon<T>(T c)
    where T : IHasHands
{
    var firstHandId = c.FirstHandID;
    var secondHandID = c.SecondHandID;

    // ...
}

Oder Du ziehst die Properties in eine Basisklasse und nimmst die als Bedingung.

PS:

Ich halte "PC" und "NPC" für grausame Klassennamen 😄

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

4.942 Beiträge seit 2008
vor 3 Jahren

Hallo und willkommen,

so funktioniert generische Programmierung nicht - alle benutzten Methoden und Eigenschaften müssen schon zur Kompilierungszeit dieser generischen Methode feststehen (falls du vorher schon mal C++ programmiert hast: dort ist es bei Templates anders).

Wenn es nur dieser eine Zugriff auf npc.FirstHandID ist, dann wäre es am einfachsten direkt diesen Wert der (nicht-generischen) Methode zu übergeben:


public static Weapon ReadDataCWeapon(int firstHandID)
{
  // ...
  if (firstHandID == ...)
  // ...
}

Du kannst dich aber auch mal bzgl. generischen Methoden über "where constraint" informieren: where (generischer Typconstraint) - auch wenn es dir hier nur bedingt helfen würde (dazu müßtest du zusätzlich ein Interface mit FirstHandID als Eigenschaft einführen und bei den Klassen benutzen).

C
CompilerSaysNo Themenstarter:in
7 Beiträge seit 2020
vor 3 Jahren

So einfach kann es sein. Ich hatte auch mit Contraints überlegt, aber mit NPC bzw. PC wäre es ja wieder auf einen Typ beschränkt. Auf die Idee mit dem Interface bin ich nicht gekommen. Vielen Dank für die großartige und schnelle Hilfe.

Oder Du ziehst die Properties in eine Basisklasse und nimmst die als Bedingung.

PS:

Ich halte "PC" und "NPC" für grausame Klassennamen 😄){gray}

Im Moment finde ich die Interface Lösung besser, als die Basisklasse, aber ich behalte es mir im Hinterkopf.

Und mit den Klassennamen hast du natürlich vollkommen Recht. Keine Ahnung, was mich da geritten hat. Eigentlich versuche ich immer sehr sprechende Namen zu wählen. Der Gedanke war wahrscheinlich schnell mal fertig machen, kann ich später ändern.^^ Habe es jetzt direkt mal geändert.

Vielen Dank nochmal.

Gruß
Maik

Hinweis von Abt vor 3 Jahren

Keine Full Quotes

C
CompilerSaysNo Themenstarter:in
7 Beiträge seit 2020
vor 3 Jahren

Wenn es nur dieser eine Zugriff auf npc.FirstHandID ist, dann wäre es am einfachsten direkt diesen Wert der (nicht-generischen) Methode zu übergeben:

Dies habe ich bewusst nicht in betracht gezogen, da ich so zwar das Problem in diesem Fall gelöst hätte, aber beim nächsten Mal vor dem gleichen Problem gestanden hätte.

Trotzdem vielen Dank für dein Feedback.

Gruß
Maik

2.080 Beiträge seit 2012
vor 3 Jahren

Der Gedanke war wahrscheinlich schnell mal fertig machen, kann ich später ändern.

Daran hält man sich nie 😄

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.