Laden...

List<string> dynamisch fest definieren

Erstellt von Krumnix vor 4 Jahren Letzter Beitrag vor 4 Jahren 2.588 Views
K
Krumnix Themenstarter:in
129 Beiträge seit 2018
vor 4 Jahren
List<string> dynamisch fest definieren

Hallo.

Wie kann ich ein List<string> beim Starten des Programms auf eine feste Größe definieren und später diese dann immer nutzen?

Ich habe eine Config-Datei in der ich die Elemente, welche ich brauche, diese jedoch von Fall zu Fall unterschiedlich groß sein können, eingetragen habe.

Nun soll beim Starten des Programms die Größe der Liste/Array festgelegt werden, sodass ich später damit anhand der im Config-File hinterlegten maximalen Größe arbeiten kann.

Z.B. soll mein List<string> 40 strings oder 30 oder 15 beinhalten, gesteuert aus der Config.
Wir kann ich dies umsetzen?

Danke für ein paar Tipps.

2.207 Beiträge seit 2011
vor 4 Jahren

Hallo Krumnix,

du kannst statt einer Liste ein Array nehmen und auf eine fixe Grösse festlegen.

Gruss

Coffeebean

K
Krumnix Themenstarter:in
129 Beiträge seit 2018
vor 4 Jahren

Hallo Coffeebean,

wie kann ich das Array aber in der Runtime verändern?
Das Programm soll ja für alle möglichen Konfigurationen funktionieren.

Für diesen Fall habe ich immer List verwendet, da ich mit .Add() neue Einträge hinzufügen kann.
Nun will ich aber diese List feste vorgeben und später mit dem Index auf die einzelnen Strings zugreifen. Beim 1. Erstellen könnte ich zwar alles mit .Add machen, aber das gefällt mir nicht, da ich dann später prüfen muss, ob ich schon einmal durch bin.

Lieber wäre mir das am Anfang einmal feste zuzuweisen und das wars 😃

2.207 Beiträge seit 2011
vor 4 Jahren

Hallo Krumnix,

willst du es per runtime verändern? Oder willst du es beim start festlegen?

Wenn du beim Start die config ausliest, weisst du ja, wieviele Einträge es sind. Dann kannst du ein Array via

int configEntryCount = /* read entries from config */
string[] myArray = new string[configEntryCount];

festlegen. Oder ist das nicht das, was du suchst?

Gruss

Coffeebean

16.806 Beiträge seit 2008
vor 4 Jahren

Kannst Du mal erklären, wofür das nützlich sein soll?
Das hört sich alles nach einem festgefahrenen, falschen Konzept an...

Das Konfigurations- und das Option-Framework in .NET / .NET Standard deckt eigentlich alle Fälle ab.
Mich würde es sehr wundern, wenn Du etwas hättest, wofür das Rad neu erfinden werden müsste.

C
26 Beiträge seit 2016
vor 4 Jahren

Technisch kannst Du das in C# mit Array.Resize() machen:

https://docs.microsoft.com/de-de/dotnet/api/system.array.resize?view=netframework-4.8

Hier findest Du eine Diskussion zu dem Thema:
https://stackoverflow.com/questions/327916/redim-preserve-in-c

Du solltest Deine Vorgehensweise vielleicht nochmal überdenken, Arrays sind schon sehr old-school und wenig flexibel.

T
2.219 Beiträge seit 2008
vor 4 Jahren

@codesoldier
Die Aussage zu Arrays solltest du nochmal überdenken.
List<T> benutzt intern auch nur ein Array und manged seine eigene Größe auch nur durch Array.Resize wenn nicht mehr genug freie Plätze im Array sind.

Arrays werden auch an vielen Stellen per Default geliefert.
Z.B. Schnittstellen bei Webservices können technisch nichts mit List<T> anfangen und erwarten hier auch Arrays als Collections.
Arrays sind genauso wie alle anderen Datentypen zeitlos.
Ansonsten müssten wir wegen "alter" Datentypen auch int und co. über Bord werfen und das Rad neuerfinden.

@Krumnix
Gibt es den Beispielcode von dir wie deine aktuelle Umsetzung aussieht?
Aktuell kann ich mir kein klares Bild machen was du damit genau erreichen willst.
Hier gibt es mit dem .NET Configuration Modell eigentlich selten etwas, was noch eine Sonderlösung bedarf.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.806 Beiträge seit 2008
vor 4 Jahren

@codesoldier
Die Aussage zu Arrays solltest du nochmal überdenken.

Im Prinzip hat er recht.
Weil der Umgang mit Arrays alles andere als modern und flexibel ist, gibt es List<T>, das Dir genau den Overhead abnimmt.

Z.B. Schnittstellen bei Webservices können technisch nichts mit List<T> anfangen und erwarten hier auch Arrays als Collections.

Das stimmt in der Form nicht.
APIs in .NET können i.d.R. problemlos implizit und explizit mit List<T> umgehen.

T
2.219 Beiträge seit 2008
vor 4 Jahren

@Abt
Klar sind Arrays unflexibel, ist auch nicht das Ziel dynamisch zu wachsen/schrumpfen.
Diese sollen auch eine fixe Breite haben.
Hat auch nichts mit modern zu tun sondern ist einfach ein grundlegendes Konzept was es auch aus guten Grund in dieser Form gibt und nicht verworfen werden kann.

Ich bezog mich mit den Webservices eben nicht star auf .NET.
Du wirst in vielen B2B Bereichen nicht mal auf .NET Treffen.
Hier habe ich überwiegend wird noch viel mit PHP/Java und SOAP zutun, was eben keine Collections in der Form anbietet.
Diese kennen eben keine List<T> sondern nur Arrays um eben Technologieneutral zubleiben.
Klar bieten die Wrapper später diese durch den generierten Proxy aber auf die Client Seite beziehe ich mich auch nicht.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.806 Beiträge seit 2008
vor 4 Jahren

Deine Beschreibung macht hier inhaltlich wenig Sinn und ist in der Form auch nicht korrekt.
Darüber hinaus etwas am Thema vorbei.

Sofern mehrere Elemente über eine neutrale Kommunikationsebene transportiert werden, wird immer auf ein Array gesetzt.
Das ist einfach ein Basistyp, der quasi in allen Sprachen vorhanden ist.

Ob es eine Abstraktion für Arrays gibt oder nicht, hat damit überhaupt nichts zutun.
In .NET gibt es das, in Java gibt es das und in weiteren Sprachen ebenfalls.
Das Framework ist dann für das explizite bzw. implizierte Type-Mappen zuständig.

Ich kann problemlos in .NET in einer Webschnittstelle List verwenden, obwohl der Gegenpart ein Array verwendet.
Damit können die Serializer in .NET fast alle umgehen. Der Verweis hier, dass irgendwelche Schnittstellen damit nichts anfangen können, ist daher inhaltlich nicht korrekt.

Und ja, in .NET ist eine Collection (welche der vielen auch immer) der modernere Ansatz um mit mehreren Elementen zu arbeiten.
Alle Collection-Typen haben ihre Daseinsberechtigung (wie das Array selbst auch), obwohl inhaltlich in quasi allen Implementierungen darunter ein Array verwendet wird.

K
Krumnix Themenstarter:in
129 Beiträge seit 2018
vor 4 Jahren

Arrays will ich nicht und habe ich auch nicht geschrieben. Also im weiteren Sinn.
Dass List<T> im Grunde auch Arrays sind, ist klar, aber für mich steht die Verwendung von List im Vordergrund.

Was ich wollte:
Mit List<T>.Capacity kann ich ja die gewünscht "Größe" definieren.
Jedoch ist mein .Count dann immer noch "0".

Wenn ich nun einen Eintrag vornehmen möchte, dann bekomme ich halt einen Fehler, da Count = 0 und ich nicht mit List<T>[1] zugreifen kann.

Durch ein XML-Konfigurations-File wird die Größe eines Byte-Streams, welcher übers Netzwerk kommt, definiert. Dieser ist unterschiedlich groß. Sprich, die jeweilige Zuordnung der Bytes zu bestimmten Werten (strings) variiert.

Beim Starten meines Programms lese ich die XML aus und weiß nun, dass der Stream 25 Strings beinhaltet. Daher möchte ich meine List<string> auf 25 definieren.
Bei einer anderen Konfig können es 30 Strings sein oder 300....
Da das Programm nicht jedesmal angepasst werden soll, wenn die Anzahl an Strings steigt, würde ich das von außen durch das XML steuern wollen.

Konkret:
Wie kann ich das elegant lösen?

Aktuelle löse ich es so:


for (int i = 0; i < XML_Library.CycleDataItemsMaxCount; i++)
                {
                    ReadItemsFromController.Add("");
                }

Funktioniert, aber ist das wirklich "gut"?!

16.806 Beiträge seit 2008
vor 4 Jahren

Daher möchte ich meine List<string> auf 25 definieren.

Warum? Was ist der Sinn?
Die Idee von List ist, dass man sich genau darum eben nicht kümmern muss / soll.

T
2.219 Beiträge seit 2008
vor 4 Jahren

@Krumnix
Count ist nur dann 0 wenn deine Liste keine Einträge hat.
Mit Capacity sagst du der Liste nur, es soll intern den Platz entsprechend reservieren.
Dies bdeutet aber nicht, dass du bei einer leeren Liste dann auch auf die Elemente direkt zugreifen kannst.
Diese müssen erst in die Liste per Add/AddRange eingetragen werden.
Anhand deines aktuellen Codes kann man schlect sagen ob es richtig oder falsch ist.
Würde aber vermuten, dass es so schon richtig ist.
Ansonsten schau dir nochmal die Doku zu List<T> an, dann wird es dir vielleicht etwas klarer.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

1.029 Beiträge seit 2010
vor 4 Jahren

Hi,

die Nachfrage von Abt kann ich hier nur unterstreichen.

Es ist ja schön und gut, dass je nach Konfiguration eine unterschiedliche Anzahl von Strings geliefert wird. Ich sehe allerdings nicht, dass dies irgendwo wirklich berücksichtigt wird.

Wenn ich dich richtig verstehe bekommst du von irgendeinem Service eine variable Anzahl von Strings - je nach Config steht auch fest, wie viele das sein sollen. Aber: Die Anzahl von Strings kommt doch so oder so - wieso also deine List<> einschränken? Das hat doch überhaupt keinen Vorteile 😕

Ich könnte es verstehen, wenn dahinter eine Art Validierung stehen soll - aber das scheint ja auch nicht der Fall zu sein.

Falls dein einziges "Problem" die Art des Zugriffs, also:

myList.Add("mystring");

an Stelle von:

myList[0] = "mystring";

ist - dann hast du kein Problem. So funktionieren Listen halt beim Hinzufügen. (Es spricht nichts gegen die Verwendung der Methode "Add" - im Gegenteil)

Falls du sicherstellen möchtest, dass dieser Liste auf keinen Fall mehr Elemente hinzugefügt werden wie konfiguriert (quasi als eine Art Validierung?) kann man das so machen wie du es gemacht hast - alternativ kann man auch eine Ableitung von List<T> schreiben, welche die Kapazität der Liste generell limitiert. Der Sinn einer solchen Validierung erschließt sich mir ad-hoc allerdings nicht.

LG

K
Krumnix Themenstarter:in
129 Beiträge seit 2018
vor 4 Jahren

Das "Problem" dabei ist, dass die Daten als Byte-Stream kommen.
Ich kennen die Anzahl an Strings, sowie die maximal definierte Länge der Strings in dem Stream.
Die tatsächlich mit Daten gefüllt Länge des Strings wird in den ersten beiden Bytes des Streams definiert.

Spricht
String 1 100 Byte -> Byte 0 + 1 -> Länge des Textes im String 1
String 2 40 Byte -> Byte 100 + 101 > Länge des Textes im String 2
String 3 90 Byte -> Byte 140 + 141 > Länge des Textes im String 3
....

String 1 ist zwar 100 Byte groß, aber wenn dort z.B. "Hello World" drin steht, dann ist die tatsächliche Größe 11. Der Stream einhaltet nämlich KEIN Abschlusszeichen, wenn ein String "fertig" ist.

Dies gehe ich nun in einer Schleife durch und möchte die Strings, welche ich so ausgewertet habe, dann jeweils immer dem richtigen "Array" in der List zuweisen. Hier ist ein .Add nicht nötig und auch nicht gewollt.....

Ich will jetzt nicht den Quatsch machen und beim 1. Durchlauf meiner Schleife dies prüfen, und dann die Init mit .Add machen, ich will, dass meine Schleife alles "fertig" nutzen kann.

Ich bleib dann bei meiner aktuellen Umsetzung. Dies scheint wohl die Lösung zu sein.
Dachte, es gibt noch eine andere Möglichkeit.

Danke für die Infos und die Erklärungen.

16.806 Beiträge seit 2008
vor 4 Jahren

Also halten wir fest: das hier ist kein technisches Problem, sondern ein Problem vom Design und der Organisation Deiner Daten.
Trotzdem macht das mit der festen Liste kein Sinn.

D
152 Beiträge seit 2013
vor 4 Jahren

Wenn es eine Liste sein muss.


IList<string> myList = new List<string>(new string[configEntryCount]);

Edit


IReadOnlyList<string> myList = new ReadOnlyCollection<string>(new string[configEntryCount]);

IReadOnlyList und ReadOnlyCollection macht hier natürlich keinen Sinn das es keinen Setter gibt.

1.029 Beiträge seit 2010
vor 4 Jahren

Hm,

das macht das Problem für mich auch nicht verständlicher 😕

Wenn die strings alle aus einem einzigen Byte-Array kommen verarbeitest du doch ohnehin schon in der richtigen Reihenfolge, womit einem .Add nichts im Wege steht.

Thema ist doch: Selbst ohne ein Endzeichen lässt sich der Bytestream ohne jede weitere Konfiguration interpretieren. Sicher - die Konfiguration lässt sich dann zur Validierung heranziehen - aber das geht ja auch problemlos in einem Durchlauf von einer Schleife - mal als Beispiel:


public static void Main(string[] args)
        {
            // config for validation
            var stringLength = new Dictionary<int, int>()
            {
                { 0, 5},
                { 1, 5 }
            };

            var testInput = new[] {"test1", "test2"};
            var dataAsBytes = testInput
                .SelectMany(s => BitConverter.GetBytes((short)s.Length).Concat(System.Text.Encoding.ASCII.GetBytes(s)))
                .ToArray();

            var parsedStrings = new List<string>();
            var index = 0;
            while (index < dataAsBytes.LongLength)
            {
                // parse
                var length = BitConverter.ToInt16(dataAsBytes, index);
                var value = System.Text.Encoding.ASCII.GetString(dataAsBytes, index + 2, length);
                parsedStrings.Add(value);
                // validate
                if (stringLength[parsedStrings.Count-1] != value.Length || parsedStrings.Count > stringLength.Count)
                {
                    throw new ArgumentException("Invalid bytestream!");
                }
                // advance
                index += length + 2;
            }
        }

5.657 Beiträge seit 2006
vor 4 Jahren

Es ist doch ganz einfach. Wenn es keine fest definierte Größe gibt, dann Initialisierung mit new List<string>() und Hinzufügen mit Add. Wenn es eine fest definierte Grüße gibt (was hier der Fall ist), dann new string[length] und Hinzufügen per Index. Wenn es trotzdem unbedingt eine Liste sein muß, kann man die initiale Größe auch im Constructor mitgeben: new List<string>(length).

Die restliche Diskussion bringt hier nicht viel. Wie man Arrays und Listen verwendet, steht in der Dokumentation. Wann man Listen oder Arrays braucht, hängt vom Anwendungsfall ab, und ist hier ziemlich eindeutig.

Es geht hier wirklich um die Grundlagen:
[FAQ] Wie finde ich den Einstieg in C#?
[Tipp] Schau in die Doku! - Möglichkeiten der Informationsgewinnung

Weeks of programming can save you hours of planning

4.931 Beiträge seit 2008
vor 4 Jahren

MrSparkle: Bei new List<string>(length) wird aber nur die Capacity gesetzt, nicht mit initialen Werten gefüllt.

Und Taipii88: Bei deinem Code solltest du die Abfrage vertauschen:


if (parsedStrings.Count >= stringLength.Count || stringLength[parsedStrings.Count-1] != value.Length)
{
    throw new ArgumentException("Invalid bytestream!");
}

(da man sonst vorher eine IndexOutOfRangeException erhält!)
Ansonsten finde ich diese Lösung sehr elegant. 😉

5.657 Beiträge seit 2006
vor 4 Jahren

MrSparkle: Bei new List<string>(length) wird aber nur die Capacity gesetzt, nicht mit initialen Werten gefüllt.

Logisch, hinzugefügt wird bei einer Liste immer mit der Add-Methode. Aber so spart man sich wenigstens das Vergrößern des internen Arrays und das Umkopieren der Werte im Hintergrund.

Weeks of programming can save you hours of planning