(object)casting ... vs (object[]) casting... was ist der Unterschied?
-
Hallo Leute!
Ich bin gerade dabei, eine kleine dateidatengestützte Anwendung zu basteln und stolpere über ein merkwürdiges Phänomen, das sich am besten direkt durch den code beschreiben lässt:
object obj1 = new object(); object[] obj2 = new object[1]; int i = 0; obj1 = i; // OK obj2[0] = i; // erzeugt Leufzeitfehler! obj2[0] = i.ToString() // OK
"Es wurde auf ein Elment zugegriffen, dessen Typ mit dem Array nicht kompatibel ist."
Wie ist das zuverstehen, und wie ist es zu lösen?
Denn wenn ich jeden Datensatz aus der Datei nur als Strings in meinen DataTable speichere, und um dann damit zu rechnen, immer von Strings zurückcasten muss, wird das ganze ziemlich performanceintensiv. Deshalb wäre mit das Speichern als Basistyp lieber. (Wahrscheinlich gibt es sowieso bessere Lösungen als mit DataTable-Objekten zu arbeiten...)
Grüße
tobi
-
Bei mir läuft der Code (VS 2008)
-
Boxing und Unboxing (C#-Programmierhandbuch)
Und vor allem: Warum nachst du kein
int
-Array (int[]
)?
-
Rhombicosidodecahedron schrieb:
Und vor allem: Warum nachst du kein
int
-Array (int[]
)?Weil ich auch andere Typen habe als
int
.Jetzt habe ich aber herrausgefunden, wo der Fehler lag - eigentlich interessant, aber eigentlich auch klar: Wenn man das
objekt
-Array vorher anders verwendet hat, kommts zu dem Typenkonflikt.
Kompletter sieht der reale code nämlich so aus:string[] buffer = File.ReadAllLines(this.strDatei); object[] otmp = new object[15]; char[] splitter = new char[1] { '\x01' }; // Aus asciiDatei in Tabelle einlesen: foreach (string line in buffer) { otmp = line.Split(splitter, 15); otmp[0] = int.Parse((string)otmp[0]); // an diese Stelle passiert der Fehler. // usw... // und am Ende steht: this.dataTable1.Rows.Add(otmp) }
Man kann also nicht einzelne Arrayelemente umcasten, wenn sie als ganzes verwendet werden... so irgendwie erkläre ich mir das.
-
Bei mir läuft der Code unter VS2010 auch.
Edit:
Bei deinem 2. Code liegt das daran, dass du deinem object-Array ein string-Array zuweißt. Als würde da stehen:object[] array = new string[5]; //oder etwas ähnliches
Split() gibt ein string-Array zurück, auf das dein object-Array dann zeigt. Somit kann es kein Integer mehr annehmen.
-
Jo, sorry Leute für die Verwirrug
Ich hab den code oben nur exemplarisch reingschrieben. Im Zusammenhang mit demstring.split(...
scheint das array irgendwie reserviert oder gesperrt zu sein. Trotzdem nicht ganz nachvollziehbar.Edit:
FreakY<3Cpp schrieb:
Split() gibt ein string-Array zurück, auf das dein object-Array dann zeigt. Somit kann es kein Integer mehr annehmen.
Ah, ok das machts klar. Ich würde also versuchen, einer Stringvariablen einen Integer zuzuweisen. Ok danke. Dann müsste es funktionieren, wenn ich mir 2 Arrays arbeite.
Edit2:
Ok, also komplett siehts jetzt so aus und geht:string[] buffer = File.ReadAllLines(this.strDatei); object[] tableline = new object[15]; string[] stmp = new string[15]; // Aus asciiDatei in Tabelle einlesen: foreach (string line in buffer) { stmp = line.Split(new char[1] { '\x01' }, 15); tableline[0] = int.Parse(stmp[0]); // id, datum tableline[1] = stmp[1]; tableline[2] = int.Parse(stmp[2]); // KW // usw... this.Rows.Add(tableline); if (this.lastID <= (int)tableline[0]) this.lastID = (int)tableline[0] + 1; }
ty:)
-
Wenn du darauf bestehst, nur mit einem Array zu arbeiten, könntest du auch das machen:
obj = line.Split(new char[1] { '\x01' }, 15).ToArray<object>();
So bleibt dein object-Array weiterhin ein Array vom Typ object.
-
du kannst auch das ohne Arrys lösen, indem du die andere Add-Mothode verwendest,und an den gewünschten Stellen der
DataRow
einen via Item (this[...])-Eigenschaften den jeweiligen Wert zuweißt.Edit: Formatierung
-
Danke Leute für Eure Tips. Ich muss jetzt erstmal weg für paar Stunden, aber ich werd mir das später mal genauer anschauen. Ich bin ja noch ziemlicher cpp-newbie
deshalb immer dankbar für Hinweise.
cu
-
TobiBob schrieb:
Ich bin ja noch ziemlicher cpp-newbie
*C# newbie :p
-
314159265358979 schrieb:
TobiBob schrieb:
Ich bin ja noch ziemlicher cpp-newbie
*C# newbie :p
:p ... wollte nicht extra dafür den post editieren
FreakY<3Cpp schrieb:
obj = line.Split(new char[1] { '\x01' }, 15).ToArray<object>();
So bleibt dein object-Array weiterhin ein Array vom Typ object.
Bist du dir da sicher? Ich habe das jetzt gerade ausprobiert, wäre ne sehr elegante Lösung, funktioniert aber nicht. Es bleiben Strings... Kann vielleicht
Split().ToArray<>()
in der Vererbungshierarchie nicht nach oben gehen?Und
obj = line.Split(new char[1] { '\x01' }, 15).Cast<object>();
wollte ich mal testen - lässt sich nicht compilieren.
@Rhombicosidodecahedron, danke, geht auch gut, kann ich bestimmt an anderer Stelle benutzen.
-
Folgendes Beispiel funktioniert:
object[] obj = new object[15]; string line = "hallo und so"; obj = line.Split(new char[1] { ' ' }).ToArray<object>(); obj[0] = "string"; obj[1] = 5;
Ich habe versucht es so sehr wie möglich wie deins aussehen zu lassen und wie gesagt, es funktioniert.
-
FreakY<3Cpp schrieb:
Ich habe versucht es so sehr wie möglich wie deins aussehen zu lassen und wie gesagt, es funktioniert.
:)Ja, aber ich möchte ja etwas in dieser Richtung machen:
object[] obj = new object[15]; string line = "1 und 1,5"; obj = line.Split(new char[1] { ' ' }).ToArray<object>(); obj[0] = (int)obj[0]; // Laufzeitfehler: angegebene Umwandlung ungültig obj[2] = (double)obj[2]; // Laufzeitfehler "
Wenn ich mir im Debuggingfenster
obj[0].GetType()
anzeigen lasse, ist das ein string. Ich will ja keine neue Referenz anlegen, sondern nur das vorhandene typecasten. ... also wenn ich mit nur 1 Array arbeite.Eigentlich sollte ich für die DataTable-Columns auch die Typen definieren. Dann geschieht bestimmt irgendne expliziete Umwandlung im Hintergrund. Ich bin jetzt aber noch nicht so weit vorgedrungen.
-
object[] obj = new object[15]; obj = line.Split(new char[1] { ' ' }).ToArray<object>();
Die erste Zeile ist sinnlos weil in der zweiten Zeile ein neues Array angelegt wird (String array um genau zu sein) und das vorherige Array ersetzt. Das object[15] array ist damit nicht mehr referenziert und kann von der GC abgeräumt werden.
Genauso gut könnte man schreiben:
object[] obj = line.Split(new char[1] { ' ' }).ToArray<object>();
Wobei Split ein String-Array liefert und kein Object-Array.
-
TobiBob schrieb:
obj[0] = (int)obj[0]; // Laufzeitfehler: angegebene Umwandlung ungültig obj[2] = (double)obj[2]; // Laufzeitfehler "
Sowas wie (int)"23" geht nicht. Du musst schon int.Parse() und double.Parse() verwenden.
-
@loks, Ok, leuchtet ein. Aber das wird ja für jede Zeile aus der Datei gemacht, und ich will nicht für jede Zeile eine neue
object[]
-Instanz anlegen.Aber dann kann ich z.b. oben auch nur schreiben
object[] obj = null;
@µ, ja ist klar, aber dann scheints komme ich doch nicht darum herum, 2 arrays zu benutzen. Oder kann ich das
string.Split()
direkt in ein DataRow-Objekt umwandeln oder einem zuweisen, wenn die columns andere Typen haben? Dann müsste ich vielleicht nochmal ein IFormatProvider-Array definieren, aber das wird dann nur umständlicher.Edit:
Aber eine andere Frage geht mir die ganze Zeit durch den Kopf: Muss ich die Daten eigentlich als chars abspeichern? Gibts nicht ne FileStream-Möglichkeit, die reinen Daten zu speichern, ohne sie in chars vorher zu konvertieren? Dann würden beim Einlesen auch die ganzen Konvertierungen wegfallen können.
-
Was spricht gegen
obj = line.Split(new char[1] { ' ' }).ToArray<object>(); obj[0] = int.Parse(obj[0]); obj[2] = double.Parse(obj[2]);
Wobei ich wahrscheinlich TryParse bevorzugen würde, da du wie es aussieht, Daten aus einer Datei ausließt und du, solange du dir nicht wirklich sicher bist, dass da auch jemand anderes dran rumfuchteln könnte, somit eine FormatException bekommen könntest.
-
@FreakY<3Cpp, *grübel* irgendwas muss ich vorher falsch gemacht haben. Jetzt gehts auch bei mir
... Danke jedenfalls.
Und hinsichtlich TryParse: Habe ich auch überlege. Das ganze ist natürlich in einem
try{}catch{}
-Block, aber wenn jemand, bzw. mein Kollege, der das beruflich braucht, in den Rohdateien rumfuscht, stimmt die ganze Auswertung nicht mehr. Da bringts dann auch nichts, wenn nur 1 Wert durch TryParse auf 0, statt auf dem originalen ist, sondern es ist besser, wenn ne anständige Fehlermeldung kommt. Vielleicht kann man die Dateien programmatisch auch mit einem schreibschutz versehen, oder ich lasse automatisch backups generieren.
So jedenfalls die Überlegung, aber das ist bestimmt auch diskussionswürdig.
-
Sein Problem ist nicht nur das er sicher sein muss das das Konvertieren Klappt, sondern auch die Feste Vorgabe das es 15 Elemente sein sollen.
Generell würde ich nicht mit Arrays arbeiten.Ich würde es in etwa so lösen (in 5-10 Minuten mal eben runter geklöppelt):
class Program { static void Main(string[] args) { object[] items = Split("This 11 is an stupid 7 example"); foreach (var item in items) { Console.WriteLine("{0} : {1}", item, item.GetType()); } } private static object[] Split(string line) { var items = new List<object>(); foreach (var part in line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) items.Add(ToConcrete(part)); return items.ToArray(); } private static object ToConcrete(string part) { if (char.IsNumber(part, 0)) // Very stupid; it checks just the first character return AsInteger(part, 0); return part; } private static object AsInteger(string number, int fallback) { int converted = fallback; if (int.TryParse(number, out converted)) return converted; return fallback; } }
Ausgabe:
This : System.String 11 : System.Int32 is : System.String an : System.String stupid : System.String 7 : System.Int32 example : System.String
Das erkennen des Types müsste man noch intelligenter machen.
Dann gibt man kein Array fester Größe vor und konvertiert nur bei Bedarf.