Structs explizite parameterlose Konstruktoren umgehen

  • C#

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Structs explizite parameterlose Konstruktoren umgehen

    Hey,

    bin gerade dabei eine Mathe-Bibliothek zu schreiben und habe Probleme eine Matrix3x3-Struct zu schreiben. Das Problem liegt darin, dass ich zwar ein multidimensionales Array erstellen kann, aber keine konkrete Elementanzahl angeben kann, da ich keine Parameter für meinen Konstruktor benötige. Sieht ungefähr so aus:

    C#-Quellcode

    1. public struct Matrix3x3
    2. {
    3. double[,] values; // = new double[3, 3] nicht möglich, da struct
    4. public int ColumnCount => 3;
    5. public int RowCount => 3;
    6. public Matrix3x3() //parameterlose explizite Konstruktoren existieren nicht in einer struct
    7. {
    8. values = new double[3, 3];
    9. }
    10. }​


    Lösungen, die ich vermeiden möchte:

    - Eine Klasse verwenden (finde ich designtechnisch unsauber, da eine Matrix3x3 sich nicht von einer anderen Matrix3x3 unterscheidet und mathematische Objekte meistens structs sind)
    - Einen unnötigen Parameter einfügen

    Gibt es da irgendein Workaround?

    Grüße
    Pascalony

    Pascalony schrieb:

    Einen unnötigen Parameter einfügen
    Klingt blöd, is aber so.
    Nimm den Wert 3 als Parameter und feddich.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Anfangs hatte ich auch immer mühe mit dem. Die Struture gibt es aber leider so vor.

    VB

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Public Module Module1
    4. Public Sub Main()
    5. Dim m3x3 = {{1.2, 2.3, 3.4}, {4.5, 5.6, 6.7}, {7.8, 8.9, 9.0}}
    6. Dim vm3x3x = New Matrix3x3V(m3x3)
    7. If m3x3.Equals(vm3x3x.Value) Then
    8. If ReferenceEquals(m3x3, vm3x3x.Value) Then
    9. Stop
    10. End If
    11. End If
    12. Dim rm3x3 = New Matrix3x3R
    13. rm3x3.Value = m3x3
    14. If m3x3.Equals(rm3x3.Value) Then
    15. If ReferenceEquals(m3x3, rm3x3.Value) Then
    16. Stop
    17. End If
    18. End If
    19. End Sub
    20. End Module
    21. Public Structure Matrix3x3V
    22. Public Value(,) As Double
    23. Public Sub New(ByVal _size As Int32)
    24. Me.Value = New Double(2, 2) {}
    25. End Sub
    26. Public Sub New(ByVal _value(,) As Double)
    27. Me.Value = _value 'Referenzbezoge
    28. 'Valuebezogen (Copy)
    29. 'Me.Value = New Double(2, 2) {}
    30. 'Array.Copy(_value, Me.Value, _value.Length)
    31. End Sub
    32. End Structure
    33. Public Class Matrix3x3R
    34. Public Value(,) As Double
    35. End Class

    CS

    C#-Quellcode

    1. using System.Diagnostics;
    2. namespace StructVsClass
    3. {
    4. class Program
    5. {
    6. static void Main(string[] args)
    7. {
    8. double[,] m3x3 = { { 1.2, 2.3, 3.4 }, { 4.5, 5.6, 6.7 }, { 7.8, 8.9, 9.0 } };
    9. Matrix3x3V vm3x3 = new Matrix3x3V(m3x3);
    10. if (m3x3.Equals(vm3x3.Value))
    11. if (ReferenceEquals(m3x3, vm3x3.Value))
    12. Debugger.Break();
    13. Matrix3x3R rm3x3 = new Matrix3x3R();
    14. rm3x3.Value = m3x3;
    15. if (m3x3.Equals(rm3x3.Value))
    16. if (ReferenceEquals(m3x3, rm3x3.Value))
    17. Debugger.Break();
    18. }
    19. }
    20. }
    21. public struct Matrix3x3V
    22. {
    23. private int sz;
    24. public double[,] Value;
    25. public Matrix3x3V(int _size)
    26. {
    27. this.sz = _size;
    28. this.Value = new double[3, 3];
    29. }
    30. public Matrix3x3V(double[,] _value)
    31. {
    32. this.sz = 3;
    33. this.Value = _value;//Referenzbezogen
    34. ////Wertebezogen (Copy)
    35. //this.Value = new double[3, 3];
    36. //Array.Copy(_value, this.Value, _value.Length);
    37. }
    38. }
    39. public class Matrix3x3R
    40. {
    41. public double[,] Value;
    42. }


    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „exc-jdbi“ ()

    C#-Quellcode

    1. [StructLayout(LayoutKind.Explicit)]
    2. unsafe struct Matrix3x3
    3. {
    4. [FieldOffset(0)]
    5. private fixed double _elements[9]; // 1d array ist mmn in vielen Fällen sinnvoller, dies ist einer davon...
    6. [FieldOffset(0)]
    7. public Vector3d Row0 { get; set; }
    8. [FieldOffset(24)]//sizeof(Vector3d) == sizeof(double) * 3
    9. public Vector3d Row1 { get; set; }
    10. [FieldOffset(48)]
    11. public Vector3d Row2 { get; set; }
    12. }


    Könnten natürlich auch columns sein, jenachdem ob deine matrix column oder row major ist..
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Wenn Deine Matrix immutable ist, was sie sein sollte, dann kannst Du den Fall, dass das Array null ist, in Deinen Berechnungen mit dem Fall gleichsetzen, dass alle Array-Elemente 0 sind.
    Wenn sie mutable ist, dann funktioniert die Struct ja unterm Strich wie eine Klasse, und dann kannste auch gleich eine Klasse daraus machen. Beachte auch, dass durch das Array sowieso immer auch was im Heap angelegt wird.

    Eine Option wäre, jedes Array-Element separat zu deklarieren. Dadurch enthält die Struct die Werte direkt, anstatt eines Verweises auf ein Array im Heap. Ist allerdings umständlich in der Handhabung.
    Das, was jvbsl gezeigt hat, macht das auf eine andere Art auch. In .NET kann man Arrays direkt in Structs "einbetten". Dafür wird allerdings unsafe benötigt, mit allen Problemen, die es so mit sich bringt.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    jvbsl schrieb:

    // 1d array ist mmn in vielen Fällen sinnvoller, dies ist einer davon...


    Kannst du erläutern, warum das hier der Fall ist? Ich würde auf unsafe halt eher verzichten wollen.

    Niko Ortner schrieb:

    Wenn Deine Matrix immutable ist, was sie sein sollte


    Ist sie. So handhabe ich alle meine "Objekte" dieser Art. Auch Complex ist bei mir eine struct. Bei manchen Dingen bietet sich OOP aber zu sehr an (z.B Cube/Cuboid).

    Niko Ortner schrieb:

    in Deinen Berechnungen mit dem Fall gleichsetzen, dass alle Array-Elemente 0 sind


    Das wäre natürlich auch eine elegante Lösung.

    Niko Ortner schrieb:

    Beachte auch, dass durch das Array sowieso immer auch was im Heap angelegt wird.


    Ist mir bewusst, aber spätestens bei einer m*n-Matrix kriege ich da Probleme wenn ich die Elemente einzeln deklariere, da gehts nur mit einem Array. Außerdem ist es dann umständlicher mit einer Schleife über die Elemente zu iterieren, außer ich würde halt einen Indexer verwenden und dann die Elemente dort einzeln ausgeben. Fände ich aber unschön.

    Edit: Damit ich nicht in jeder Methode auf null testen muss, teste ich jetzt einfach im Indexer auf null und greife über this[x, y] statt über values[x, y] auf die Elemente zu:

    C#-Quellcode

    1. public struct Matrix3x3 : IMatrix<double>
    2. {
    3. double[,] values;
    4. public int ColumnCount => 3;
    5. public int RowCount => 3;
    6. public double this[int x, int y]
    7. {
    8. get
    9. {
    10. if (values == null) values = new double[3, 3];
    11. return values[x, y];
    12. }
    13. set
    14. {
    15. if (values == null) values = new double[3, 3];
    16. values[x, y] = value;
    17. }
    18. }
    19. }

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Pascalony“ ()

    Ich sehe keinen Grund auf unsafe verzichten zu wollen bis auf: "Ich kann es nicht" Sollte dies der Fall sein, dann lerns :P Ich halte es für sinnvoller, da man eindeutig sehen kann in welcher Reihenfolge die Daten im Speicher liegen und somit auch überprüfen, ob es column oder row major ist. Und da es sich sowieso um ein Grid handelt ;) Der jitter konnte mal besser mit 1D arrays für manche Algorithmen besser optimieren(keine Ahnung in wiefern das immer noch stimmt)...
    Abgesehen davon, dass du dir mit o.g. Code es sehr einfach machst um die Rows(oder evtl. Columns jenach dem) rauszuholen und das auch noch mit der bestmöglichsten performance...

    @Niko Ortner: mit der immutability lässt sich mMn darüber streiten. In den meisten Fällen stimme ich dir zu, doch wenn man etwas oft mutated dann sollte der jitter besser optimieren können(muss aber zugeben, dass ich es nie überprüft habe)...

    Spätestens bei einer MxN Matrix machst du dir eine automatische Code generierung die gecached wird ;) Wobei ab einer bestimmten größe stack tatsächlich keinen Sinn mehr ergibt...

    Edit: achja vorsicht mit interfaces bei structs, kann zu Problemen führen wenn jemand über die interfaces arbeitet, dann machste dir die ganze performance kaputt, weil das geboxt werden muss...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    jvbsl schrieb:

    Ich sehe keinen Grund auf unsafe verzichten zu wollen bis auf: "Ich kann es nicht" Sollte dies der Fall sein, dann lerns


    Eigentlich habe ich kein Problem mit unsafe, nur Kommentare wie "mit allen Problemen, die es so mit sich bringt" und angebliche Sicherheitslücken etc. haben mich etwas abgeschreckt ^^ Ich bin der letzte der sich weigern würde etwas dazu zu lernen.

    jvbsl schrieb:

    achja vorsicht mit interfaces bei structs, kann zu Problemen führen wenn jemand über die interfaces arbeitet, dann machste dir die ganze performance kaputt, weil das geboxt werden muss...



    Ja da hatte ich auch meine Bedenken. Generell habe ich eher Probleme eine gute Struktur aufzubauen. Z.b wäre es extrem einfach eine Matrix-Klasse zu haben mit GetTranspose, GetInverse, Multiply usw. und dann einfach eine Matrix3x3-Klasse die davon erbt. Aber das widerspricht eben der Immutability. Aber ohne ein Interface könnte ich bei structs keine generische Implementierung von Funktionen wie SameType (gleiche Menge an Columns, Rows) benutzen. Dann müsste ich für Matrix-Matrix3x3, Matrix-Matrix4x4, Matrix-ComplexMatrix und alle anderen möglichen Kombinationen eine eigene Methode schreiben, was auch wieder unsauber ist imo. Im Moment überlege ich deshalb echt ob ich nicht einfach eine Klasse verwenden soll um mir den Aufwand zu sparen. Deine Lösung ist zwar durchaus möglich, aber das ändert nichts an den anderen Design-technischen Problemen. Ich erinner mich, dass Artentus bei MathUtils ähnliche Probleme hatte, das Konzept in structs umzusetzen.

    Unsafe hat nur Sicherheitsprobleme wenn du es falsch anwendest und z.B. über Memorygrenzen liest/schreibst. Einfach verhindern dass soetwas passiert und alles ist gut.

    Nunja es wird einmal alles programmiert was du für die structs brauchst, wenn du das als unschön empfindest, was ich durchaus verstehen kann, dann würde ich dir empfehlen ein T4 Template zur Code generierung zu schreiben.
    Interfaces kannst du ansich ja trotzdem weiterhin verwenden. Jedoch würde ich dir trotzdem empfehlen keine Methoden zu schreiben, die dann IMatrix zurückgeben oder als Parameter haben.

    Aber wirst du jemals mehrere Matrizen, die diesselbe Matrix implementieren haben?
    Wenn ja, warum machst du dann nicht eine implizite Konvertierung, oder werden diese Typen nicht zur Compiletime bekannt sein?
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    jvbsl schrieb:

    Jedoch würde ich dir trotzdem empfehlen keine Methoden zu schreiben, die dann IMatrix zurückgeben oder als Parameter haben.



    Das Problem, das ich dann habe ist, dass ich verschiedene Algorithmen dann dreifach implementieren muss. Z.b das Inverse einer Matrix, dann müsste ich den Algorithmus für die Rückgabetypen MatrixMxN, Matrix3x3, Matrix4x4 etc implementieren.

    jvbsl schrieb:

    Aber wirst du jemals mehrere Matrizen, die diesselbe Matrix implementieren haben?


    Also meinst du jetzt verschiedene Matrix Instanzen, die IMatrix<double> implementieren? In manchen Szenarien bestimmt möglich. Z.B wenn du einen Vector durch eine Matrix4x4 translatest und dann mit einer Matrix3x3 rotatest. Natürlich sind so Szenarien eher abwegig.

    jvbsl schrieb:

    Wenn ja, warum machst du dann nicht eine implizite Konvertierung, oder werden diese Typen nicht zur Compiletime bekannt sein?



    Möglich wäre es. Das Ding ist halt, dass ich nur eine MathLibrary programmiere und die Nutzung so flexibel wie möglich halten will ohne, dass sich jemand Extensions schreiben muss. Ich muss mir das nochmal genauer überlegen, ob ich es jetzt schön mit einer Klasse umsetzte, oder aufwendig aber dafür eben wie es eigentlich sein sollte.

    Wie gesagt automatische Codegenerierung mit T4 Templates, dann musste die Algorithmen nicht mehrfach schreiben und hast sogar noch den Vortei, dass du die Codeerzeugung komplett auf die spezialisierten Matrizen anpassen kannst, was dazu führt, dass du automatisiert den Code etwas optimieren kannst(Schleifen z.B aufdrößeln um die Sprünge zu sparen, mehrfach Berechnungen über temporäre Variablen vermeiden etc...)

    Edit: sollte denk ich mit partial gehen, dass du das Zeug, was wirklich spezifisch nur für einen Typ deklariert wird eben in eine Datei kommt und der Rest über das Template generiert wird, wobei man solche Spezielisierungen auch direkt in T4 machen kann, was da jetzt schöner ist bin ich mir selbst nicht so sicher^^. Aber CrossProduct gibts z.B. in Vec2 höchstens ohne parameter und gibt (-y,x) oder so zurück
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „jvbsl“ ()

    jvbsl schrieb:

    Wie gesagt automatische Codegenerierung mit T4 Templates



    Hast du dazu eine gute Quelle? Hab ich noch nie benutzt.

    jvbsl schrieb:

    Aber CrossProduct gibts z.B. in Vec2 höchstens ohne parameter und gibt (-y,x) oder so zurück



    Bei Vektoren hab ich keine Probleme das in structs umzusetzen komischer Weise. ^^

    msdn.microsoft.com/de-de/library/gg586945.aspx

    Ich weiß das hättest auch selbst gefunden, aber mehr hab ich eigt. nie gebraucht, wenn ich was spezielles gebraucht hab, dann hab ich gegooglet.
    Im Prinzip ist es nur ein kleines C# script welches dann Text erzeugt und in diesem Fall halt eine .cs datei die dann mit kompiliert wird...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    jvbsl schrieb:

    h weiß das hättest auch selbst gefunden, aber mehr hab ich eigt. nie gebraucht



    Wird bestimmt reichen, danke :D

    ErfinderDesRades schrieb:

    Die ist sehr fein zum transformieren von Grafik, da kann ich Verschiebungen, Drehungen, Skalierungen je nach Bedarf dran einstellen.



    Kann meine Matrix auch. Nach dem Vorbild von Vector.UnitX als static Property gibts das auch mit Rotation-, Scaling-, Shearing- und Translation-Matrix. Dann multiplizierste deine Matrix oder den Vektor einfach mit der Rotationmatrix und hast das gleiche Ergebnis. Aber wie gesagt, ich bin mir ja selbst noch nicht schlüssig ob Klasse/struct.

    Pascalony schrieb:

    ich bin mir ja selbst noch nicht schlüssig ob Klasse/struct.
    Mach im Zweifelsfalle ne Klasse draus.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!