none
Wie erstelle ich ein IEnumerator? RRS feed

  • Frage

  • Hallo Community,

    ich habe folgenden Fehler:

    Schweregrad	Code	Beschreibung	Projekt	Datei	Zeile	Unterdrückungszustand
    Fehler	CS1579	Eine foreach-Anweisung kann nicht für Variablen vom Typ "TeamSpeak3QueryApi.Net.Program.Structs.BotConnections" verwendet werden, da "TeamSpeak3QueryApi.Net.Program.Structs.BotConnections" keine öffentliche Definition für "GetEnumerator" enthält.	TeamSpeak3QueryApi.Program	C:\Users\lgund\Downloads\C-Querybot-master\C-Querybot-master\TeamSpeak3QueryApi.Program\Program.cs	103	Aktiv
    

    Ich bin allerdings hoffnungslos überforder, wie ich bei BotConnections so ein IEnumerator hinzufügen kann. Ich hoffe ihr könnt mir da weiter helfen...

    Meine BotConnections:

    using System.Collections.Generic;
    using TeamSpeak3QueryApi.Net.Specialized;
    
    namespace TeamSpeak3QueryApi.Net.Program.Structs
    {
        class BotConnections
        {
            public int id { get; set; }
            public int instance { get; set; }
            public List<SidInfo> sids { get; set; }
            
            public class SidInfo
            {
                public bool isActive { get; set; }
                public int lastSeen { get; set; }
                public int id { get; set; }
                public string botname { get; set; }
                public bool isWelcomeMessage { get; set; }
                public string welcomeMessage { get; set; }
                public bool isAfkMove { get; set; }
                public int afkMoveChannel { get; set; }
                public TeamSpeakClient teamspeak { get; set; }
            }
        }
    }
    

    Dienstag, 16. Mai 2017 12:52

Antworten

  • Hallo,

    das Ganze sieht nach dem ersten Szenario aus, das ich beschrieben hatte.

    Erstelle in der Klasse, die die BotConnections verwaltet eine List<BotConnection> (s bei Klasse streichen):

    private List<BotConnection> BotConnections = new List<BotConnection>();

    Damit gibt es den Enumerator frei Haus und Du kannst damit die einzelnen Verbindungsinstanzen verwalten (Add, Remove usw.), genauso wie Du es bereits mit der SidInfo Liste tust.

    Zum restlichen Code: Es ist vergleichsweise ineffizient für jede einzelne SidInfo eine neue Abfrage zu starten. Dabei ist i. a. der Overhead für das Erstellen und Ausführen der Abfrage und das Übertragen der Daten (vor allem in Netzwerk) höher, als wenn man alles auf einen Rutsch abfragt.

    Da der Aufbau vermuten lässt, dass die SidInfo mit der BotConnection zusammenhängen und die abhängigen Daten über die BotConnection.Id bzw. BotConnection.Instance identizifiert werden können,  verwende sie als Abfragekriterien, so dass alle verwendeten Daten (keine *)  geliefert werden.

    Derzeit hast Du einen Cross Join in deiner Abfrage, d. h. gibt es mehr als eine Zeile, so werden gleiche Daten etliche Male abgerufen. Gibt es Beziehungen bei den Tabellen zueinander verwende einen INNER/LEFT JOIN, oder führe die einzelnen Abfrage in einem Stapel ab. Bei letzterem hat der Reader mehrere Ergebnis (NextResult).

    Angedeutet der Code dazu:

    // TODO: * vermeiden, nur verwendete Daten abrufen
    // TODO: Verwenden von INNER JOIN oder mehreren Abfragen (derzeit CROSS JOIN)
    command.CommandText = "SELECT con.id, con.botname, pat.*, afk.*, messages.* " 
       + "FROM bot_connections con, bot_bad_pattern pat, bot_settigns_afk afk, bot_settings_messages messages "
       + "WHERE con.id=@botid AND pat.id=@botid AND afk.id=@botid AND messages.id=@botid;";
    command.Parameters.Clear();
    SqlParameter botid = command.Parameters.Add("@botid", SqlDbType.Int);    // SQL Server angenommen, andere entsprechend
    foreach (BotConnection Bot in BotConnections)    // siehe oben
    {
        foreach (BotConnections.SidInfo Server in Bot)
        {
            botid = Bot.id
            // lokale Variable reicht
            using (var reader =  command.ExecuteReader())    // impliziert Dispose (auch bei Exception)
            {
                while(reader.Read())
                {
                    // sinnvolles damit anfangen
                }
            }
        }
    }
    
    Gruß Elmar
    Mittwoch, 17. Mai 2017 06:56
    Beantworter

Alle Antworten

  • Hi,

    poste bitte mal deinen Code, der den Fehler verursacht.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Dienstag, 16. Mai 2017 12:55
    Moderator
  • Hallo First-Coder,

    Du implementierst die IEnumerator Schnittstelle am besten so wie in der MSDN beschrieben und mit Beispiel gezeigt: IEnumerator<T>-Schnittstelle

    Sollte das nicht helfen, konkretisiere deine Frage bitte.


    - Gruß Florian

    Dienstag, 16. Mai 2017 13:42
  • Hallo,

    deine Klasse BotConnections ist keine Auflistung (Array, Collection, List). Nur diese haben von Haus einen Enumerator, da sie dafür bestimmt sind mehrere gleichartige Instanzen aufzunehmen.

    Wenn Du dem Namen nach mehrere "BotConnection" Instanzen verwenden willst, so verwende eine List<BotConnection> und erstelle sie als Instanz in der verwaltenden Klasse. Auf das irreführende Plural-"S" solltest Du in dem Fall verzichten.

    Oder aber leite "BotConnection(s)" von List<T> oder einer anderen Auflistung ab. Wobei das, nach den verwendeten Eigenschaften (ID, Instance) zu urteilen, vermutlich nicht Deine Absicht sein wird.

    Gruß Elmar

    Dienstag, 16. Mai 2017 17:11
    Beantworter
  • Den meisten Quelltext habt ihr ja schon, der Aufruf mit der foreach sieht so aus:

                    foreach (BotConnections Bot in BotConnections)
                    {
                        foreach (BotConnections.SidInfo Server in Bot)
                        {
                            command.CommandText = "SELECT con.id, con.botname, pat.*, afk.*, messages.* FROM bot_connections con, bot_bad_pattern pat, bot_settigns_afk afk, bot_settings_messages messages WHERE con.id =" + Bot.id + " AND pat.id =" + Bot.id + " AND afk.id =" + Bot.id + " AND messages.id =" + Bot.id;
                            Reader = command.ExecuteReader();
                            while (Reader.Read())
                            {
                                for (int i = 0; i < Reader.FieldCount; i++)
                                {
                                    Log.Write(Log.Level.Debug, Bot.id + ": Found " + Reader.GetName(i).ToString() + " Value: " + Reader.GetString(i).ToString());
                                }
                            }
                            command.Dispose();
                            Reader.Dispose();
                        }
                    }

    Die Instanz hat mehrere serverids (sids), die ich mit einer extra foreach abfragen wollte. Leider bin ich noch neu in der Sprache, ich hoffe ihr nimmt es nicht übel.

    Dienstag, 16. Mai 2017 18:16
  • Hallo,

    das Ganze sieht nach dem ersten Szenario aus, das ich beschrieben hatte.

    Erstelle in der Klasse, die die BotConnections verwaltet eine List<BotConnection> (s bei Klasse streichen):

    private List<BotConnection> BotConnections = new List<BotConnection>();

    Damit gibt es den Enumerator frei Haus und Du kannst damit die einzelnen Verbindungsinstanzen verwalten (Add, Remove usw.), genauso wie Du es bereits mit der SidInfo Liste tust.

    Zum restlichen Code: Es ist vergleichsweise ineffizient für jede einzelne SidInfo eine neue Abfrage zu starten. Dabei ist i. a. der Overhead für das Erstellen und Ausführen der Abfrage und das Übertragen der Daten (vor allem in Netzwerk) höher, als wenn man alles auf einen Rutsch abfragt.

    Da der Aufbau vermuten lässt, dass die SidInfo mit der BotConnection zusammenhängen und die abhängigen Daten über die BotConnection.Id bzw. BotConnection.Instance identizifiert werden können,  verwende sie als Abfragekriterien, so dass alle verwendeten Daten (keine *)  geliefert werden.

    Derzeit hast Du einen Cross Join in deiner Abfrage, d. h. gibt es mehr als eine Zeile, so werden gleiche Daten etliche Male abgerufen. Gibt es Beziehungen bei den Tabellen zueinander verwende einen INNER/LEFT JOIN, oder führe die einzelnen Abfrage in einem Stapel ab. Bei letzterem hat der Reader mehrere Ergebnis (NextResult).

    Angedeutet der Code dazu:

    // TODO: * vermeiden, nur verwendete Daten abrufen
    // TODO: Verwenden von INNER JOIN oder mehreren Abfragen (derzeit CROSS JOIN)
    command.CommandText = "SELECT con.id, con.botname, pat.*, afk.*, messages.* " 
       + "FROM bot_connections con, bot_bad_pattern pat, bot_settigns_afk afk, bot_settings_messages messages "
       + "WHERE con.id=@botid AND pat.id=@botid AND afk.id=@botid AND messages.id=@botid;";
    command.Parameters.Clear();
    SqlParameter botid = command.Parameters.Add("@botid", SqlDbType.Int);    // SQL Server angenommen, andere entsprechend
    foreach (BotConnection Bot in BotConnections)    // siehe oben
    {
        foreach (BotConnections.SidInfo Server in Bot)
        {
            botid = Bot.id
            // lokale Variable reicht
            using (var reader =  command.ExecuteReader())    // impliziert Dispose (auch bei Exception)
            {
                while(reader.Read())
                {
                    // sinnvolles damit anfangen
                }
            }
        }
    }
    
    Gruß Elmar
    Mittwoch, 17. Mai 2017 06:56
    Beantworter