marcoborn
Goto Top

Verschachtelte Arrays

Hallo Forum,
ich habe in VB.NET 6 Arrays, die alle dieselbe Länge, aber unterschiedliche Datentypen haben. Array1 ist vom Typ Long, Array2 String, Array3 Long, Array4 Long, Array5 String und Array6 ist String.
In einer Schleife werden die Arrays befüllt und anschließend wird jedes Array zuerst in ein 2-dimensionales Array kopiert, das dann in einen Excel-Range übertragen wird.

Stattdessen möchte ich ein verschachteltes Array nutzen, wobei die Schleife jeweils ein Array mit 6 Werten befüllt. Dieses Array ist Element eines übergeordneten Arrays, das dann am Ende wieder nach Excel kopiert wird.

Ist es in VB.NET machbar, dass in einem Array die Elemente unterschiedliche Datentypen haben? Aufgrund der Arraygrößen würde ich ungern mit dem Object-Datentyp arbeiten wollen. Gibt es ggf. Alternativen zu meinem Ansatz, die ähnlich performant sind?

Vielen Dank,
M. Born

Content-ID: 270597

Url: https://administrator.de/contentid/270597

Ausgedruckt am: 24.11.2024 um 14:11 Uhr

Clijsters
Clijsters 30.04.2015 um 15:00:47 Uhr
Goto Top
Hallo Marco,

Könntest Du vielleicht ein paar Codeschnipsel anfügen?
Das würde uns die Lösungsfindung ungemein erleichtern.

Ich habe das nun in etwa so verstanden:
Array1[123, 456, 435, 6537...], Array2["asd", "ert"...]
Und daraus würde dann werden:
Gesamt[Arr1[Array1], Arr2[Array2], ...]

Wäre das dann der Ist- oder der Soll-Zustand?

Was ist denn das allgemeine Ziel des ganzen? Vielleicht gäbe es ja auch eine ganz andere Herangehensweise, welche sich als gut herausstellt.


Beste Grüße
Dominique
MarcoBorn
MarcoBorn 04.05.2015 um 08:58:16 Uhr
Goto Top
Hallo Dominique,
zur Zeit läuft mein Code wie folgt ab:
Ich habe zur Zeit ein zweidimensionales Array, das über eine Schleife mit Werten gefüllt wird.

Dim myArr(länge, 5) As String
For i As Integer = 1 To länge
  myArr(i - 1 - offset, 0) = i
  myArr(i - 1 - offset, 1) = abs.Range.ListFormat.ListString
  myArr(i - 1 - offset, 2) = level
  myArr(i - 1 - offset, 5) = Left(abs.Range.Text, vbCr, "")  
Next

In einer späteren Schleife werden dann auch die Spalten 3 und 4 gefüllt.

Anschließend wird das gesamte Array in einen Zellbereich in Excel eingefügt:
appxl.ActiveSheet.Cells(1, 1).Resize(länge, 6).Value = myArr

Sowohl FxCop als auch Tools zur Codeanalyse empfehlen, statt dem zweidimensionalen Array verschachtelte Arrays einzusetzen. Bevor ich aber meinen ganzen Code umbaue (oben ist nur ein sehr kurzer Ausschnitt) würde ich gern wissen, ob verschachtelte Arrays unterschiedliche Datentypen besitzen können.

Vielen Dank,
M. Born
Clijsters
Clijsters 04.05.2015 um 10:24:04 Uhr
Goto Top
Hallo Marco,

grundsätzlich kannst du Arrays als Object deklarieren und somit in den Items verschiedene Datentypen verwenden.

Ich würde auch eher verschachtelte Arrays mit jeweils definierten Datentypen verwenden.
Somit hättest du ein Array für die Tabelle, welches zeilenweise Arrays besitzt, dessen Items die jeweiligen Spalten darstellen.
Das Konzept kannst Du natürlich auch umdrehen und definierte Datentypen verwenden.

Damit arbeitet es sich leichter und Modellnäher.
Schau mal hier

Beste Grüße
Dominique
MarcoBorn
MarcoBorn 04.05.2015 um 13:36:06 Uhr
Goto Top
Hallo Dominique,
den Weg über Object-Arrays möchte ich vermeiden, da ich den ganzen Aufwand mit den Arrays nur deswegen treibe, um die Performance zu erhöhen und den RAM-Verbrauch (z.Zt. mehr als 900 MB) zu senken. Bei der Menge an Daten, die verarbeitet werden müssen, ist das ganze Boxing bei Object's völlig kontraproduktiv.
In Deinem Link wird leider nur auf Object-Arrays eingegangen.

Gibt es ggf. noch völlig andere Ansätze außer Arrays? Structures scheinen mir auch nicht für meine Problem geeignet zu sein. Was könnte man noch ausprobieren?

Viele Grüße,
M. Born
Clijsters
Clijsters 04.05.2015 um 14:46:25 Uhr
Goto Top
Erkläre uns doch etwas genauer, was der Kern deiner Anwendung ist.
Also, was machst du, was möchtest du erreichen?

Dann fällt es uns leichter, etwas passendes zu finden.
Es gibt diverse Wege, Datenstrukturen schnell, schlank oder praktisch abzubildden...


LG
Dominique
MarcoBorn
MarcoBorn 04.05.2015 um 15:05:42 Uhr
Goto Top
Ich lese Daten in einem .NET-Program aus Word aus, verarbeite diese weiter und schreibe sie anschließend in eine Excel-Tabelle. Aufgrund von Performance-Problemen habe ich die zellenweise Speicherung der Daten durch die zuvor genannten Arrays ersetzt und kopiere das Gesamt-Array erst am Ende des Projekts nach Excel. Dadurch konnte ich ca. 20% Performancesteigerung erzielen. Aber ein Durchlauf dauert immer noch ca. 1h und ich suche Wege, die Schleife zu optimieren.
Clijsters
Clijsters 04.05.2015 um 16:42:30 Uhr
Goto Top
Ok, um was für Daten handelt es sich denn?
Wie viel ist es, wie wird es unter Word und wie unter Excel repräsentiert?
Eventuell postest du etwas mehr von deinem Code? Oder beispielhafte Dateien?

Mir scheint es, als seien es nicht nur die Datentypen oder die Strukturierung der Arrays, die dir Performance klauen.

Beste Grüße
Dominique
MarcoBorn
MarcoBorn 05.05.2015 um 08:51:17 Uhr
Goto Top
Guten Morgen,

es hängt davon ab, welche Word-Datei importiert wird. Die Struktur ist immer gleich, aber die Daten selbst können völlig unterschiedlich sein. Z.B. kann die Länge der Strings auch mehr als 1000 Zeichen umfassen.

Ich definiere jeweils ein Word- und Excel-Object und über Late Binding werden die entsprechenden Dateien vom User über FileOpen-Dialoge ausgewählt. Der Code selbst umfasst mehr als 10.000 Zeilen und dient hauptsächlich zu anderen Zwecken und hat mit der o.g. kaum was zu tun.

Das Array kann durchaus 2000 Zeilen umfassen, hat aber immer nur die 6 Spalten wie zuvor beschrieben. Um die Performance zu erhöhen, habe ich schon die Datentypen alle sauber definiert und keine Objekt-Variablen mehr im Code drin. Am Ender jeder Prozedur setze ich auch alles wieder auf Nothing. Dadurch konnte ich die Performance schon etwas steigern.

Irgendwo scheint ein Memory Leak aufzutreten, aber ich habe noch keinen Profiler gefunden, der eine XLL sauber profilen kann. Daher versuche ich, mit kleinen Verbesserungen am Code den Speicherverbrauch zu reduzieren und das Tool effizienter zu machen.

Viele Grüße,
M. Born
MarcoBorn
MarcoBorn 07.05.2015 um 08:04:04 Uhr
Goto Top
Kann mir niemand helfen?
Clijsters
Clijsters 07.05.2015 um 08:34:12 Uhr
Goto Top
Zitat von @MarcoBorn:

Kann mir niemand helfen?
Doch, sicher.
Ich fragte ja bereits nach einer Datenstruktur, dem Code oder anderen Anhaltspunkten, die nahe legen, was du vorhast.
MarcoBorn
MarcoBorn 08.05.2015 um 07:55:50 Uhr
Goto Top
Steht ja alles schon in meinen bisherigen Posts.

Datenstruktur Ist: 2-dimensionales Array vom Typ String, wobei einzelne Spalten Integer und Long enthalten
Datenstruktur Soll: Verschachteltes Array, wobei die Unterarrays jeweils verschiedene Datentypen enthalten
Code: Siehe Post vom 04.05., 08:58

Meine Frage: Ist es in VB.NET machbar, dass in einem Array die Elemente unterschiedliche Datentypen haben? Aufgrund der Arraygrößen würde ich ungern mit dem Object-Datentyp arbeiten wollen. Gibt es ggf. Alternativen zu meinem Ansatz, die ähnlich performant sind?
Clijsters
Clijsters 08.05.2015 um 09:46:08 Uhr
Goto Top
Steht ja alles schon in meinen bisherigen Posts.
Nein.
Datenstruktur Ist: 2-dimensionales Array vom Typ String, wobei einzelne Spalten Integer und Long enthalten
Wow, wie erstellt man denn solche Word-Dokumente?
Deinem Code ist zu entnehmen, dass das Array mit Werten aus Variablen befüllt wird.
Aber was für Values die Member dann während der Laufzeit bekommen (könnten), erschließt sich meiner Glaskugelmagie.
Code: Siehe Post vom 04.05., 08:58
Das ist nicht sehr vielsagend. Du kannst Kommentare auch verlinken
Meine Frage: Ist es in VB.NET machbar, dass in einem Array die Elemente unterschiedliche Datentypen haben?
Ja.
Aufgrund der Arraygrößen würde ich ungern mit dem Object-Datentyp arbeiten wollen.
Dann bau dir was definierteres.
Gibt es ggf. Alternativen zu meinem Ansatz, die ähnlich performant sind?
Ja.
Von hier:
Structures scheinen mir auch nicht für meine Problem geeignet zu sein. Was könnte man noch ausprobieren?
Warum nicht? Das ist doch genau, was du suchst.
Fest definierte Member, kein wahlloses Object, feste Breite, ...

Und was ist mit Mutithreading?
Ein Thread könnte immer eine Menge an Daten einlesen, zerpflücken und an einen zweiten geben, der das Sheet füllt, während der erste wieder dabei ist, zu lesen.
Wenn du eine definierte Menge hast, platzt dein RAM auch nicht so schnell aus allen Nähten.
Weiter oben hast du ja beschrieben, dass du nun darin über gegangen bist, erst komplett einzulesen und dann in's Sheet zu schreiben.
Nicht, dass dein Rechner jenach Datenmenge die Auslagerungswut bekommt face-wink

Beste Grüße
Dominique
MarcoBorn
MarcoBorn 08.05.2015 um 15:29:38 Uhr
Goto Top
Es sind ganz normale Word-Dokumente, die eine gewisse Struktur besitzen müssen. In anderen Teilen des Codes werden diese geparst. In meinem Code-Beispiel steckt in der Variable abs ein Objekt, das jeweils einen Word-Absatz beinhaltet. So lese ich die Formatierung und den Text des Absatzes aus. Die Word-Dateien werden "von Hand" durch die Kollegen befüllt.

Die Variable "abs.Range.ListFormat.ListString" kann jeder String sein, der auch als Bezeichnung für eine Formatierung in Word benutzt werden kann. Da die Dateien z.T. auch durch Kunden von uns bearbeitet werden, kann da alles mögliche drin stehen, weil jeder Kunde sich ja eigene Formatierungen definieren kann und die ggf. beim Kopieren auch in unserer Datei landen.

Die Variable "abs.Range.Text" liest den Text eines Absatzes aus, kann also u.U. ein ziemlich langer String sein. Der Inhalt ist für mein Tool auch völlig egal, da ich lediglich nicht sichtbare Sonderzeichen entferne und ihn dann in eine Zelle in Excel schreibe.

Die Variable i ist eine Zahl von 1 bis x, wobei x die Anzahl der Absätze in Word darstellt (meist deutlich kleiner als 1000, aber durchaus auch bis 5000).

Die Variable level steht für eine Zahl von 1 bis 9.

Der Vorschlag, Object-Arrays kam ja von Dir. Wenn ich einen Weg finde, das Ganze mit Arrays von String, Long und Integer abzubilden, kommt mir das sehr entgegen. In Deinem Link (https://msdn.microsoft.com/de-de/library/8k8021te%28v=vs.90%29.aspx) wird ein Object-Array angelegt, dann werden dort verschiedene Datentypen reingeschrieben und letztlich wird über CInt und CStr auf die Elemente zugegriffen, also genau das was ich nicht will: Object-Arrays und Typumwandlungen.

Ich habe Structures bereits getestet. Ich kann problemlos die Werte hineinschreiben, kann diese aber nur Element-weise wieder auslesen. Ein Schreiben per Block nach Excel wird mit einem Laufzeitfehler abgebrochen.

Multithreading hilft mir auch nicht weiter, weil ich dann auch wieder nur zeilenweise schreiben kann. Bei der Kommunikation zwischen Excel und meinem .NET-Addin vergeht bei jedem Zugriff auf Excel zuviel Zeit. Allein das Blockweise Kopieren statt dem zellenweisen Beschreiben hat einen Geschwindigkeitsgewinn von rund 20% ausgemacht (bezogen auf das Gesamt-Makro, nicht nur die Schleife).


Letztlich interessiert mich lediglich die Aussage, ob ich verschachtelte Arrays bauen kann, bei denen z.B. ein inneres Array Strings und ein weiteres Longs aufnimmt und wie ich dann das äußere Array definiere.
Clijsters
Clijsters 08.05.2015 um 16:30:26 Uhr
Goto Top
Das sagt doch schonmal deutlich mehr face-smile

mir das sehr entgegen. In Deinem Link (https://msdn.microsoft.com/de-de/library/8k8021te%28v=vs.90%29.aspx) wird ein Object-Array angelegt,
Ja, das war aber auch bevor du erwähntest, dass Objects nicht in Frage kommen und was du genau vorhast.
Ich habe Structures bereits getestet. Ich kann problemlos die Werte hineinschreiben, kann diese aber nur Element-weise wieder auslesen. Ein Schreiben per Block nach Excel wird mit einem Laufzeitfehler abgebrochen.
Element-weise?
Letztlich interessiert mich lediglich die Aussage, ob ich verschachtelte Arrays bauen kann, bei denen z.B. ein inneres Array Strings und ein weiteres Longs aufnimmt und wie ich dann das äußere Array definiere.
Mein Vorschlag: Eine Structure, die "innere" Typisierte Arrays aufnimmt. Oder auch ein 1-Dimensionales Array aus den selbstdefinierten Structures.
Die einzige "Aufgabe" dahinter ist das Nachgehen der Frage, warum eine Exception geworfen wird, wenn du "Blockweise" schreiben möchtest.
Bei der Kommunikation zwischen Excel und meinem .NET-Addin vergeht bei jedem Zugriff auf Excel zuviel Zeit.
Wäre es nicht genau dann von Vorteil, wenn die Schleife selbst nicht genau dieser verstrichenen Zeit zum Opfer fällt?
MarcoBorn
MarcoBorn 11.05.2015 um 09:15:01 Uhr
Goto Top
Bei den Structures kann ich über eine Schleife auf einzelne Elemente zugreifen, aber nicht auf die gesamte Struktur. Ich komme also beim Kopieren nach Excel nicht ohne Schleife aus. Daher fällt der Weg über eine Structure aus Arrays weg. Ein Array von Structures wäre ggf. ein Weg, der sich lohnen könnte. Das werde ich mal probieren.

Ich versuche ja, Schleifen soweit möglich zu eliminieren. Das Füllen des Arrays geht leider nur Schleifen-basiert, aber das Kopieren nach Excel erfolgt als Block.
MarcoBorn
MarcoBorn 11.05.2015 um 09:49:54 Uhr
Goto Top
Leider erhalte ich hier wieder dasselbe Problem: Greife ich testweise per Schleife auf einzelne Zeilen des Arrays zu, kann ich die Werte der Structure einfach auslesen.
For i As Long = 1 To 10
  MsgBox(TestArr(i).Struct1 & chr(13) & TestArr(i).Struct2)
Next

Wenn ich das komplette Array nach Excel kopiere, bekomme ich wieder die Fehlremeldung:
Falscher Parameter. (Ausnahme von HRESULT: 0x80070057 (E_INVALIDARG))
bei Microsoft.VisualBasic.CompilerServices.Symbols.Container.InvokeMethod(Method TargetProcedure, Object Arguments, Boolean CopyBack, BindingFlags Flags)

bei Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateSet(Object Instance, Type Type, String MemberName, Object Arguments, String ArgumentNames, Type TypeArguments, Boolean OptimisticSet, Boolean RValueBase, CallType CallType)

bei Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateSetComplex(Object Instance, Type Type, String MemberName, Object Arguments, String ArgumentNames, Type TypeArguments, Boolean OptimisticSet, Boolean RValueBase)

bei FKGenDNA.MboImportClass.MboWordStrukturCheck() in C:\Users\Born\Documents\SharpDevelop Projects\fkgendna_v2\mbo_Import.vb:Zeile 2428.

Wenn ich nicht eine Struktur als Ganzes auslesen kann, komme ich so nicht weiter...
Clijsters
Lösung Clijsters 11.05.2015, aktualisiert am 12.05.2015 um 10:02:21 Uhr
Goto Top
Wenn ich nicht eine Struktur als Ganzes auslesen kann, komme ich so nicht weiter...
Dann mag Excel das wohl garnicht.

Ich habe mal ein bisschen herumgewälzt.
Wie es scheint, arbeitet die Excel Interoperabilitätsklasse sowieso mit Objects.
Dein zweidimensionales Array, wie du es weiter oben beschrieben hast, scheint also soweit mir ersichtlich, die beste Variante zu sein.
Ob du nun aus dem String-Array ein Object-Array machst oder nicht... Das ist dem anderen Ende scheinbar schnuppe. Um ein Casting kommst du (wenn ich mich nicht irre) sowieso nicht herum.
Vor allem sehe ich weiter oben, dass du Integer in dein String-Array pumpst. Was würde es also für einen Unterschied machen? face-wink


Vor allem ist das zweidimensionale Array auch für Excel hinter dem Interop sehr viel einfacher in eine Range zu setzen als ein verschachteltes Array.

Nun nochmal zu der eigentlichen Performancefrage...
Ich denke, dass dir in diesem Fall das Typisieren zumindest bei der Übertragung in das Excel sheet keinen großen Performancevorteil bringt.

Möchtest du hingegen vorher in deinem Code noch eine Menge mit den Arrays machen (weshalb du vlt. angemakelt hast, dass du mittels Casting darauf zugreifen müsstest) und hättest dafür gerne etwas typisiertes, könntest du dein Array vorher umdrehen.

Du könntest also erstmal mit spaltenweisen Arrays arbeiten:
Dim spalte0(länge) As Integer
Dim spalte1(länge) As String
Dim spalte2(länge) As Integer
Dim spalte3(länge) 'As ???  
Dim spalte4(länge) 'As ???  
Dim spalte5(länge) As String

'Optional kannst du diese 6 Arrays in ein weiteres (Object) oder eine Structure packen.  
'Dim myTable(6) as Object() = {spalte0, spalte1, spalte2, spalte3, spalte4, spalte5}  
Dann muss das ganze nur für Excel passend gemacht werden:
Public Function umdrehen(ByRef first As Integer(), ByRef second As String()[...]) As Object()
	Dim fullTable(länge, 6) As Object 'Hat die Tabelle nun 5 oder 6 Spalten?  
	For i As Integer = 0 to länge - 1
		fullTable(i, 0) = first(i)
		fullTable(i, 1) = second(i)
		fullTable(i, 2) = third(i)
		'[...]  
		'fullTable könnte auch ein String-Array werden.  
	End For
	Return fullTable
End Function

Aber, wie mir das klingt, wird das alles nie wirklich "schnell" werden. Hier ist VB.Net eventuell nicht das richtige. Oder Excel, oder Word...
Inwieweit z.B. mittels C# Zeiger verwendet werden könnten, um die Daten hin und herzugeben, ist mir nicht klar. Dafür mache ich zu wenig mit Excel.

Um deine eigentliche Frage zu beantworten:
Bei der von dir angegebenen rechteckigen Datenstruktur würde ein verschachteltes Array dir keinen Vorteil zu einem 2-dimensionalen Array bringen.

Beste Grüße
Dominique
MarcoBorn
MarcoBorn 12.05.2015 um 10:02:07 Uhr
Goto Top
Guten Morgen,
im Moment arbeite ich nur mit einem String Array, in das ich Integers packe, weil ich keinen Weg gefunden habe, in ein 2-dimensionales Array mehrere Datentypen zu packen.Im Umwandeln der Arrays sehe ich keinen Vorteil hinsichtlich der Performance, da hier auch wieder eine Schleife benötigt wird. Da ich nicht direkt mit den Interop-Klassen von MS arbeite, sondern mit NetOffice, weiss ich nicht genau, wie das dort intern interpretiert wird. Ich habe aber keinen Performance-Unterschied zwischen MS Excel und NetOffice feststellen können, so dass ich davon ausgehe, dass NetOffice dasselbe Datenmodel intern verwendet wie MS.

Ich glaube, dass ich hier kaum weiterkomme und versuche, in anderen Stellen im Code noch Optimierungsmöglichkeiten zu finden.

Danke für die Hilfe,
M. Born
Clijsters
Clijsters 12.05.2015 um 11:54:52 Uhr
Goto Top
Hallo MarcoBorn,

Da ich nicht direkt mit den Interop-Klassen von MS arbeite, sondern mit NetOffice, weiss ich nicht genau, wie das dort intern interpretiert wird
Ich persönlich arbeite auch lieber mit NetOffice. Davon gibt's ein GitHub-Repo
Und unter Source/Excel/Interfaces findest du in der IRange.cs in den Zeilen 569, 2299 und 2327 die Funktionen, welche du aufrufst, wenn du auf Range.Value zugreifst.
Ich habe mir das nicht mehr genauer angesehen, vielleicht hilft dir das aber mal.

Ich glaube, dass ich hier kaum weiterkomme und versuche, in anderen Stellen im Code noch Optimierungsmöglichkeiten zu finden.
Gut, viel Erfolg. Eventuell gibst du ja mal Feedback, wenn etwas interessantes dabei rumkommt.

Danke für die Hilfe,
M. Born
Naja, ich hab's wenigstens versucht face-wink

Beste Grüße
Dominique