Ich habe ein Problem, bei dem ich mir nicht mehr zu helfen weis.
1. Ich habe eine Collection (abgeleitet von CollectionBase) erstellt
public class Contur : CollectionBase
..
..
public int Add(Frame value)
{
return List.Add(value);
}
2. Frame ist eine Basisklasse, die einfach ein 2-Dimensionales Single Array implementiert (also ein Referenztyp)
3. Das Problem:
Contur points = new Contur();
foreach (string s in source)
{
..
Frame point = new Frame(.....);
points.Add(point);
}
Wenn ich das so durchlaufen lasse, haben alle Werte in der Collection points schlussendlich die gleichen Werte, obwohl das Frame point jeweils nachweislich unterschiedlich ist. Ich vermute, das ist ein Problem mit Referenztypen, was mir aber suspekt erscheint, weil ich ja denn point immer neu deklariere und instanziere.
Als Versuch habe ich eine Methode Clone für den Typ Frame programmiert, die alle Einträge einzeln kopiert (also die Wertetypen innerhalb des Frames). Dies verusacht jedoch in der Schleife einen Stack-Overflow ?!? Die Clone Methode wende ich aber auch anderen Orten an, wo sie einwandfrei funktioniert.
Also, ich stehe wie der Esel am Berg. Kann mir jemand einen Tipp geben?
Absolut. Habe mir beispielsweise in der Schleife die einzelnen point's jeweils mit einer MessageBox anzeigen lassen. Diese waren immer korrekt.
Die ganze Funktion ist ein Parser. Das heisst es werden aus einem File die einzelnen Zeilen ausgelesen. Der schlussendliche Inhalt der Collection ist jetzt falscherweise immer die letzte Zeile des Files. Er nimmt die Punkte richtig auf, überschreibt aber mit Hinzufügen (Add) alle bestehenden Werte der Collection mit dem neuen Wert.
Ich habe vorhin bei der Frame Klasse die Funktion GetHashCode überschrieben, so dass ich sicher sein kann, dass die einzelnen Instanz auseinandergehalten werden. Hat aber nichts gebracht.
Irgendwo steckt der Fehler genau in dem Code, den du uns nicht zeigst. Aber eine Frage: Warum machst du dir die Mühe eine eigene Collection zu basteln?
Oder anders gefragt: Warum nicht
List<Frame< points = new List<Frame>();
foreach (string s in source)
{
..
Frame point = new Frame(.....);
points.Add(point);
}
Nun gut, es ist nicht so dass ich euch Code vorenthalten möchte. Aber ich kann mir schlicht und einfach nicht vorstellen wo der Bug sein könnte. Schliesslich werden keine dabei keine weiteren Funktionen aufgerufen oder auf andere Referenzen zugegriffen.
Wieso ich eine eigene Collection "gebastelt" habe? Ich muss mit der Liste von Frames weitere Operationen machen, die ich so sehr einfach implementieren kann. Eine blosse Liste wie eine ArrayList oder gar ein Frame Array sind dazu schlecht geeignet. Zum Verständniss der Klassennamen: Frame beschreibt einen geometrischen Punkt und Contur eine Sammlung von Punkten.
Ausserdem habe ich schon mehrmals von CollectionBase abgeleitet und dabei keine Probleme gehabt.
Wenn du eine Idee hast, wo das Problem liegen könnte, werde ich den entsprechenden Code natürlich gerne posten. Ansonsten, die Klassen sind relativ umfangreich.
Ich sehe jetzt gerade: Du hast eine Lösung mit einer generischen Liste vorgeschlagen. Ich muss leider (noch) .NET 1.1 verwenden :-(
Wie gesagt, den Stück Code den du gepostet hast, der stimmt vom Prinzip her. Der Fehler ist im anderen Teil. Wie schaust du dir denn die Werte nachm einfügen an? Ist vielleicht einfach das Auslesen der Werte fehlerhaft? Wird noch irgendwo anders auf die Collection schreibend drauf zugegriffen?
Das ist ja das komische: Direkt nach dem Befüllen tritt dieser Effekt ein. Ich habe es auf jede erdenkliche Art und weise überprüft. Wenn ich z.B. bei jedem Schleifendurchgang den ersten Wert der Collection abfrage, müsste dieser immer gleich sein. Er ist jedoch gleich dem zuletzt hinzugefügten.
Das schlimmste ist, dass ich mittlerweile keine Ahnung mehr habe, was ich noch überprüfen könnte....
Ich würde vorschlagen wenn du beim Einfügen nichts findest, schau mal beim Ausgeben nach. Vieleicht liegt der Fehler dort. Beim Enumerator oder beim Indexer. Prüf bei der Ausgabe auch mal, ob dort die Frames nicht nur vom Inhalt gleich sind, oder vieleicht sogar die gleiche Referenz haben ( == Operator, oder object.ReferenceEquals )
Der Zugriff auf die einzelnen Stellen (this[,] Eigenschaft) erfolgt dabei so:
public float this[int Row, int Column]
{
get
{
if ((Row < this.Rows) && (Column < this.Columns))
{
return i_matrix[Row,Column];
}
else
{
throw new IndexOutOfRangeException(this.Rows.ToString() + "x" + this.Columns.ToString() + " Matrix");
}
}
}
wobei
float[,] i_matrix;
Ich habe auch schon direkt auf das Array zugegriffen. Wenn man die Einträge in der Collection (points) überprüft, resultiert folgendes:
Beim == Vergleich (selber programmiert, vergleicht einfach die einzelnen Einträge) : Ist für jeden Vergleich true
Der Vergleich mit object.ReferenceEquals ergibt nur true, wenn mit dem selben Objekt verglichen wird, sonst nicht. Das wäre also korrekt.
Wie gesagt, ich setze die Klassen und die gleichen Funktionen auch anderen Orten erfolgreich ein. Meiner Meinung nach "spinnt" die List der Collection...
@edit
Mit dem Enumerator kann es ja nichts zu tun haben. Der ist ja in CollectionBase (welche ja lediglich eine Implementierung einer ArrayList) beinhaltet, bereits definiert. Den habe ich nicht angerührt.
Mit der Schleife hat es auch nichts zu tun. Dieser Code, was zeigt er an??
Contur c = new Contur();
Frame a = new Frame(0.1f, 0.3f, 0.4f, 0.1f, 0.6f, 0.3f, 0.1f, 0.7f, 0.8f, 1.0f, 2.0f, 3.0f);
Frame b = new Frame(0.4f, 0.2f, 0.7f, 0.3f, 0.8f, 0.6f, 0.8f, 0.2f, 0.3f, 6.0f, 8.0f, 2.0f);
Frame d = new Frame(0.6f, 0.6f, 0.9f, 0.5f, 0.2f, 0.7f, 0.9f, 0.1f, 0.6f, 8.0f, 7.0f, 7.0f);
c.Add(a); c.Add(b); c.Add(d);
MessageBox.Show(c[0].ToString());
Ich hab mal dein Beispiel nachgebaut mit ner Frameklasse die nen internes Floatarray hat und so arbeitet wie dein Frame, und hab der Einfachheit halber ne List<Frame> genommen und des durchprobiert und es funktioniert so wie gedacht. Deshalb denke ich es liegt an deiner Collection die wohl falsch implementiert ist. Oder immer noch am Einfügen...
Kannst du vielleicht den kompletten relevanten Code posten?
Also ich bin nun auch etwas weitergekommen. Ich kann nun die Collection als Fehlerquelle ausschliessen. Ich habe nämlich versucht, meine Frames einer simplen ArrayList hinzuzufügen, wobei dann immer der gleiche Effekt eintrat (alte Werte werden überschrieben). Dann habe ich der ArrayList Instanzen einer anderen aber ähnlichen Klasse hinzugefügt und wieder ausgelesen - es funktionierte prima. Die andere Klasse ist soweit identisch, nur verwendet sie statt 2D Array ein 1D Array.
Der Fehler liegt meines Erachtens nun in der Klasse Frame. Wobei es da doch noch etwas spezielles gibt: diese ist nämlich eine Ableitung einer anderen Klasse (von der Klasse Matrix). Frame ist einfach eine 4x4 Matrix.
Nachfolgend poste ich diese beiden Klassen (Matrix und Frame). Die Operatoren und die statischen Funktionen lasse ich wegen des Umfangs weg (sind ja statisch).
public class Matrix
{
public Matrix() { }
public Matrix(int Rows, int Columns)
{
i_matrix = new float[Rows, Columns];
this.Fill(0.0f);
}
protected float[,] i_matrix = new float[0,0];
public static readonly Matrix Identity4 = new Matrix(new float[,] {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}});
public int Columns
{
get { return i_matrix.GetLength(1); }
}
public int Rows
{
get { return i_matrix.GetLength(0); }
}
public float this[int Row, int Column]
{
get
{
if ((Row < this.Rows) && (Column < this.Columns))
{
return i_matrix[Row,Column];
}
else
{
throw new IndexOutOfRangeException(this.Rows.ToString() + "x" + this.Columns.ToString() + " Matrix");
}
}
set
{
if ((Row < this.Rows) && (Column < this.Columns))
{
i_matrix[Row,Column] = value;
}
else
{
throw new IndexOutOfRangeException(this.Rows.ToString() + "x" + this.Columns.ToString() + " Matrix");
}
}
}
public void Fill(float Val)
{
for (int x = 0; x < this.Rows; x++)
{
for (int y = 0; y < this.Columns; y++)
{
this[x,y] = Val;
}
}
}
public Matrix Clone()
{
return (Matrix) this.MemberwiseClone();
}
public float[,] ToArray()
{
return i_matrix;
}
public float[] ToArray(int Column)
{
if (Column < this.Columns)
{
float[] result = new float[this.Rows];
for (int i = 0; i < this.Rows; i++)
{
result[i] = this[i,Column];
}
return result;
}
else
{
throw new IndexOutOfRangeException();
}
}
public override string ToString()
{
System.Text.StringBuilder s = new System.Text.StringBuilder();
s.Append(this.Rows.ToString() + "x" + this.Columns.ToString() + " Matrix\n");
for (int x = 0; x < this.Rows; x++)
{
string row = "|";
for (int y = 0; y < this.Columns; y++)
{
row += this[x,y].ToString();
row += ", ";
}
row = row.Remove(row.Length-2, 2);
row += "|\n";
s.Append(row);
}
return s.ToString();
}
public override bool Equals(object obj)
{
Matrix mat = obj as Matrix;
if (mat != null)
return (this == mat);
else
return false;
}
public override int GetHashCode()
{
int hash = this.Length;
for (int x = 0; x < this.Rows; x++)
{
for (int y = 0; y < this.Columns; y++)
{
hash ^= i_matrix[x,y].GetHashCode();
}
}
return hash;
}
public class Frame : Matrix
{
public Frame()
{
i_frame = Matrix.Identity4.Clone();
base.i_matrix = i_frame.ToArray();
}
public Frame(Matrix Rotation, Vector Translation)
{
i_frame = Matrix.Identity4.Clone();
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
i_frame[i,j] = Rotation[i,j];
}
i_frame[i,3] = Translation[i];
}
base.i_matrix = i_frame.ToArray();
}
public Frame(Vector Axis, float Angle, Vector Translation)
{
// Rotationsmatrix berechnen
Axis.Norm();
Matrix matrix = new Matrix(3,3);
matrix[0,0] = (float) (Math.Cos(Angle)+Math.Pow(Axis.X,2)*(1.0f-Math.Cos(Angle)));
matrix[0,1] = (float) (Axis.X*Axis.Y*(1.0f-Math.Cos(Angle))-Axis.Z*Math.Sin(Angle));
matrix[0,2] = (float) (Axis.X*Axis.Z*(1.0f-Math.Cos(Angle))+Axis.Y*Math.Sin(Angle));
matrix[1,0] = (float) (Axis.Y*Axis.X*(1.0f-Math.Cos(Angle))+Axis.Z*Math.Sin(Angle));
matrix[1,1] = (float) (Math.Cos(Angle)+Math.Pow(Axis.Y,2)*(1.0f-Math.Cos(Angle)));
matrix[1,2] = (float) (Axis.Y*Axis.Z*(1.0f-Math.Cos(Angle))-Axis.X*Math.Sin(Angle));
matrix[2,0] = (float) (Axis.Z*Axis.X*(1.0f-Math.Cos(Angle))-Axis.Y*Math.Sin(Angle));
matrix[2,1] = (float) (Axis.Z*Axis.Y*(1.0f-Math.Cos(Angle))+Axis.X*Math.Sin(Angle));
matrix[2,2] = (float) (Math.Cos(Angle)+Math.Pow(Axis.Z,2)*(1.0f-Math.Cos(Angle)));
// Frame erstellen
i_frame = Matrix.Identity4.Clone();
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
i_frame[i,j] = matrix[i,j];
}
i_frame[i,3] = Translation[i];
}
base.i_matrix = i_frame.ToArray();
}
public Frame(Convention Convention, Vector Rotation, Vector Translation)
{
// Rotationsmatrix berechnen
Matrix matrix = new Matrix(3,3);
switch (Convention)
{
case Convention.XYZ :
{
matrix[0,0] = (float) (Math.Cos(Rotation.Y) * Math.Cos(Rotation.Z));
matrix[0,1] = (float) (Math.Cos(Rotation.Y) * Math.Sin(Rotation.Z));
matrix[0,2] = (float) (Math.Sin(Rotation.Y));
matrix[1,0] = (float) (Math.Cos(Rotation.X) * Math.Sin(Rotation.Z) * -1.0f - Math.Sin(Rotation.X) * Math.Sin(Rotation.Y) * Math.Cos(Rotation.Z));
matrix[1,1] = (float) (Math.Cos(Rotation.X) * Math.Cos(Rotation.Z) - Math.Sin(Rotation.X) * Math.Sin(Rotation.Y) * Math.Sin(Rotation.Z));
matrix[1,2] = (float) (Math.Sin(Rotation.X) * Math.Cos(Rotation.Y));
matrix[2,0] = (float) (Math.Sin(Rotation.X) * Math.Sin(Rotation.Z) - Math.Cos(Rotation.X) * Math.Sin(Rotation.Y) * Math.Cos(Rotation.Z));
matrix[2,1] = (float) (Math.Cos(Rotation.X) * Math.Sin(Rotation.Y) * Math.Sin(Rotation.Z) * -1.0f - Math.Sin(Rotation.X) * Math.Cos(Rotation.Z));
matrix[2,2] = (float) (Math.Cos(Rotation.X) * Math.Cos(Rotation.Y));
break;
}
case Convention.ZYX :
{
matrix[0,0] = (float) (Math.Cos(Rotation.Y) * Math.Cos(Rotation.Z));
matrix[0,1] = (float) (Math.Cos(Rotation.X) * Math.Sin(Rotation.Z) - Math.Sin(Rotation.X) * Math.Sin(Rotation.Y) * Math.Cos(Rotation.Z));
matrix[0,2] = (float) (Math.Cos(Rotation.X) * Math.Sin(Rotation.Y) * Math.Cos(Rotation.Z) + Math.Sin(Rotation.X) * Math.Sin(Rotation.Z));
matrix[1,0] = (float) (Math.Cos(Rotation.Y) * Math.Sin(Rotation.Z) * -1.0f);
matrix[1,1] = (float) (Math.Cos(Rotation.X) * Math.Cos(Rotation.Z) + Math.Sin(Rotation.X) * Math.Sin(Rotation.Y) * Math.Sin(Rotation.Z));
matrix[1,2] = (float) (Math.Sin(Rotation.X) * Math.Cos(Rotation.Z) - Math.Cos(Rotation.X) * Math.Sin(Rotation.Y) * Math.Sin(Rotation.Z));
matrix[2,0] = (float) (Math.Sin(Rotation.Y) * -1.0f);
matrix[2,1] = (float) (Math.Sin(Rotation.X) * Math.Cos(Rotation.Y) * -1.0f);
matrix[2,2] = (float) (Math.Cos(Rotation.X) * Math.Cos(Rotation.Y));
break;
}
default :
{
throw new NotImplementedException("Konvention " + Convention.ToString() + " noch nicht implementiert");
}
}
// Frame erstellen
i_frame = Matrix.Identity4.Clone();
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
i_frame[i,j] = matrix[i,j];
}
i_frame[i,3] = Translation[i];
}
base.i_matrix = i_frame.ToArray();
}
public Frame(Matrix Translation)
{
if (Translation.IsSquare && Translation.Dimension == 4)
{
i_frame = Translation;
}
else if (Translation.IsSquare && Translation.Dimension == 3)
{
i_frame = Translation.OverMatrix(4,4);
i_frame[3,3] = 1.0f;
}
else
{
i_frame = Matrix.Identity4.Clone();
}
base.i_matrix = i_frame.ToArray();
}
public Frame(float F00, float F10, float F20,
float F01, float F11, float F21,
float F02, float F12, float F22,
float F03, float F13, float F23)
{
i_frame = Matrix.Identity4.Clone();
i_frame[0,0] = F00; i_frame[0,1] = F01; i_frame[0,2] = F02; i_frame[0,3] = F03;
i_frame[1,0] = F10; i_frame[1,1] = F11; i_frame[1,2] = F12; i_frame[1,3] = F13;
i_frame[2,0] = F20; i_frame[2,1] = F21; i_frame[2,2] = F22; i_frame[2,3] = F23;
base.i_matrix = i_frame.ToArray();
}
private Matrix i_frame;
private Matrix i_rot;
private Vector i_trans;
private int i_id;
private string i_tag = "";
private State i_state = State.BASE;
public static readonly Frame Identity = new Frame(new float[,] {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}});
public new float this[int Row, int Column]
{
get
{
if ((Row < this.Rows) && (Column < this.Columns))
{
return i_frame[Row,Column];
}
else
{
throw new IndexOutOfRangeException(this.Rows.ToString() + "x" + this.Columns.ToString() + " Matrix");
}
}
set
{
if ((Row < this.Rows) && (Column < this.Columns))
{
i_frame[Row,Column] = value;
base.i_matrix = i_frame.ToArray();
}
else
{
throw new IndexOutOfRangeException(this.Rows.ToString() + "x" + this.Columns.ToString() + " Matrix");
}
}
}
public Matrix Rotation
{
get { return this.GetRotation(); }
set { for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
i_frame[i,j] = value[i,j];
}
base.i_matrix = i_frame.ToArray();
}
}
public Vector Position
{
get { return this.GetPosition(); }
set { for (int i = 0; i < 3; i++)
i_frame[i,3] = value[i];
base.i_matrix = i_frame.ToArray();
}
}
public State FrameState
{
get { return i_state; }
set {i_state = value; }
}
public int ID
{
get { return i_id; }
set { i_id = value; }
}
public string Tag
{
get { return i_tag; }
set { i_tag = value; }
}
public Frame Inversed
{
get { return this.Inverse(); }
}
private Matrix GetRotation()
{
return new Matrix(3,3);
}
private Vector GetPosition()
{
return (i_trans = new Vector(this[0,3],this[1,3],this[2,3]));
}
public new Frame Clone()
{
return (Frame) this.MemberwiseClone();
}
public new Frame Inverse()
{
return new Frame();
}
public string ToMatrixString()
{
System.Text.StringBuilder s = new System.Text.StringBuilder();
s.Append(this[0,0].ToString() + " ");
s.Append(this[1,0].ToString() + " ");
s.Append(this[2,0].ToString() + " ");
s.Append(this[0,1].ToString() + " ");
s.Append(this[1,1].ToString() + " ");
s.Append(this[2,1].ToString() + " ");
s.Append(this[0,2].ToString() + " ");
s.Append(this[1,2].ToString() + " ");
s.Append(this[2,2].ToString() + " ");
s.Append(this[0,3].ToString() + " ");
s.Append(this[1,3].ToString() + " ");
s.Append(this[2,3].ToString());
return s.ToString();
}
}
Bemerkung: In der Klase Frame hatte ich zuerst noch keine interne Variable i_frame (ich habe saubererweise immer auf base.i_matrix zugegriffen). Auch die Neuimplementierung des Indexierers habe ich aufgrund der Probleme mit Listen gemacht.
Hehe, des ist wirklich nen vertrickter Nebeneffekt Ohne Code hätt man lange raten können. Es liegt an dem kleinen aber feinen Schlüsselwort static. Die meisten kennen es einfach daher das man keine Instanzen brauch um drauf zuzugreifen, aber es beinhaltet noch nen kleinen aber feinen anderen Aspekt: Static impliziert auch das es immer nur eine Variable davon gibt! Und da du alle Werte auf dein static Array mappst verwenden natürlich alle Instanzen deiner Frameklasse immer die gleichen Werte.
Hab dir mal Code angehangen der genau dieses Verhalten provoziert:
using System;
namespace ConsoleApplication4 {
class Program {
static void Main(string[] args) {
Test t = new Test(new int[] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 });
Test t2 = new Test(new int[] { 1, 2, 3, 4, 5, 0, 9, 8, 7, 6 });
Console.WriteLine("T: {0}", t);
Console.WriteLine("T2: {0}", t2);
Console.ReadLine();
}
}
public class StaticTest {
public static int[] test = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };
public override string ToString() {
string ret = "";
foreach (int i in StaticTest.test) {
ret += i.ToString() + ":";
}
return ret;
}
}
public class Test : StaticTest {
public Test(int[] zahlen) {
StaticTest.test = zahlen;
}
}
}
ist bei dir nicht ganz so offensichtlich weil der Code halt umfangreicher ist, aber genau das passiert bei dir Deshalb löst sich auch das Problem wenn du deine Matrix Klasse nicht mehr verwendest udn damit auch nicht mehr das statische Array.