AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein [.NET] Generics und Operatorüberladung
Thema durchsuchen
Ansicht
Themen-Optionen

[.NET] Generics und Operatorüberladung

Ein Thema von Medium · begonnen am 10. Jul 2009 · letzter Beitrag vom 11. Jul 2009
Antwort Antwort
Seite 1 von 3  1 23      
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#1

[.NET] Generics und Operatorüberladung

  Alt 10. Jul 2009, 20:11
Moinmoin!

Ich implementiere gerade eine Matrix-Klasse in C#, und brauche diese mit generischem Typ. Hintergrund ist, dass eine Matrix wiederum Matrizen als Elemente beinhalten können muss, dies aber nicht zwangsweise tut. Es kommen also auch normale Typen wie Double oder Integer vor.
Das Problem hier ist nun, dass der Compiler (zurecht) meckert, dass die Operatoren die ich da überlade nicht unbedingt für den Typ <T> vorhanden sind. Ich suche nun also eine Möglichkeit, dem Compiler klar zu machen, dass dort lediglich Typen kommen, für die die Operatoren sehr wohl definiert sind.

Hier mal mein grobes Codegerüst (unwichtigen Krams ausgelassen):

Code:
   
public class Matrix<T>
{
   private T[,] M;
   
   public Matrix(int sizeX, int sizeY)
   {
      M = new T[sizeX, sizeY];
   }
   
   public Matrix(T[,] init)
   {
      M = (T[,])init.Clone();
   }
   
   // Und hier knallt es dann natürlich, da nicht alle Nachkommen von Object + rechnen können
        public static Matrix<T> operator +(Matrix<T> a, Matrix<T> b)
   {
      if ((a.XDimension != b.XDimension) || (a.YDimension != b.YDimension)) throw new ArrayTypeMismatchException("Operation + on mismatching matrices");
      else{
         Matrix<T> t = new Matrix<T>(a.XDimension, a.YDimension);
         for (int x=0; x<t.XDimension; x++)
            for (int y=0; y<t.YDimension; y++)
               t[x, y] = a[x, y] + b[x, y];
         return t;
      }
   }
}
Ich habe bis jetzt kein Interface oder Constraint gefunden, mit dem ich das realisieren kann. Gibbets da wat?
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Dax
(Gast)

n/a Beiträge
 
#2

Re: [.NET] Generics und Operatorüberladung

  Alt 10. Jul 2009, 20:18
Nein. Du musst einen Wrapper (Structs, die nur deinen Typen enthalten, bieten sich da an) definieren, der ein entsprechendes Interface implementiert.

add: Diese Structs haben dann idealerweise implizite Konversionen zum/vom gewrapten Typ, dann merkt man das (fast) nicht, nur bei der Deklaration der Matrix.
  Mit Zitat antworten Zitat
Elvis

Registriert seit: 25. Nov 2005
Ort: München
1.909 Beiträge
 
Delphi 2010 Professional
 
#3

Re: [.NET] Generics und Operatorüberladung

  Alt 10. Jul 2009, 20:30
Zitat von Dax:
Nein. Du musst einen Wrapper (Structs, die nur deinen Typen enthalten, bieten sich da an) definieren, der ein entsprechendes Interface implementiert.
add: Diese Structs haben dann idealerweise implizite Konversionen zum/vom gewrapten Typ, dann merkt man das (fast) nicht, nur bei der Deklaration der Matrix.
ValueTypes, die in Interfaces geboxt werden, werden aber leider... geboxt.
Es wird sich wohl eher lohnen Klassen für den Wrapper herzunehmen.
man kann es auch so lösen, dass du Delegates für einen Satz T hinterlegst, die wiederum den Code des Operators enthalten.
Mit ein paar Tricks kostet dich das fast gar nix zur Laufzeit.

Ich muss jetzt aber los, vllt kann ich es vor Montag noch genauer zeigen...
Robert Giesecke
I’m a great believer in “Occam’s Razor,” the principle which says:
“If you say something complicated, I’ll slit your throat.”
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#4

Re: [.NET] Generics und Operatorüberladung

  Alt 10. Jul 2009, 20:31
Verdammt, ich bin auch gerade auf diverse Seiten gestoßen wo sich groß darüber ausgelassen wird, dass das ein riesen Leck in C# ist. Seit Jahren schon, und trotz vieler vorgeschlagener Wege das zu umgehen, ist keiner davon für mich wirklich geeignet (bzw. die die es wären, sind teils vollständige Parser mit 1000+ Zeilen... zu langsam, zu komplex - Sitchwort LINQ).

Warum zum Geier stoße ich bei Fun-Projekten immer schon nach unter einer Stunde an die Grenzen meiner Tools?

Ein einfaches Interface "IArithmetic" dass alle numerischen Typen implementieren wäre doch so eine tolle Sache. Aber Dax, was du da vorschlägst klingt nicht uninteressant, jedoch habe ich gerade keine Vorstellung davon wie das in Code gegossen aussehen könnte. (Ich bin noch nicht allzu geübt in C#.) Magst du mir ein kurzes strukturelles Beispiel zeigen?

\\Edit: Elvis' roter Kasten is kaputt
Um ehrlich zu sein wäre Boxing ein vergleichsweise kleiner Tradeoff. Wie sähe sowas aus? Auch die Variante mit den Delegates klingt nicht unspannend!
Einfach wäre es ja, wenn T nur von mir erstellte Typen beinhalten würde. Irgendwie muss ich da nur noch die primitiven mit rein kriegen...
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Dax
(Gast)

n/a Beiträge
 
#5

Re: [.NET] Generics und Operatorüberladung

  Alt 10. Jul 2009, 20:37
Zitat von Elvis:
ValueTypes, die in Interfaces geboxt werden, werden aber leider... geboxt.
Es wird sich wohl eher lohnen Klassen für den Wrapper herzunehmen.
Wenn er schon mehrdimensionale Arrays benutzt, tut das Boxen auch nicht mehr weh Davon abgesehen werden Interfacecalls auf Valuetypes meiner Erinnerung nach doch in ein load address + static call übersetzt? Zumindest macht der gmcs das so
  Mit Zitat antworten Zitat
Dax
(Gast)

n/a Beiträge
 
#6

Re: [.NET] Generics und Operatorüberladung

  Alt 10. Jul 2009, 21:10
Ein Beispiel für beides:
Code:
interface IArithmetic<T, V>
{
   T Add(T left, T right);
   V Value {
      get;
      set;
   }
}

struct DoubleArithmetic : IArithmetic<DoubleArithmetic, double>
{
   public double Value {
      get;
      set;
   }

   public DoubleArithmetic Add(DoubleArithmetic left, DoubleArithmetic right)
   {
      return new DoubleArithmetic { Value = left.Value + right.Value };
   }

   public static implicit operator DoubleArithmetic(double value)
   {
      return new DoubleArithmetic { Value = value };
   }

   public static implicit operator double(DoubleArithmetic da)
   {
      return da.Value;
   }
}

class Combiner<T>
{
   public Func<T, T, T> Add {
      get;
      private set;
   }

   public Combiner(Func<T, T, T> _add)
   {
      Add = _add;
   }
}

public class MainClass
{
   static void test<T, V>(V inc) where T : IArithmetic<T, V>, new()
   {
      var d = DateTime.Now;
      T da = new T();
      int max = 10000000;
      for (int i = 0; i < max; i++) {
         da = da.Add(da, new T { Value = inc });
      }
      Console.WriteLine((DateTime.Now - d).TotalMilliseconds / max * 1000000);
   }
   
   static void test2<T>(T inc, Combiner<T> ops) where T: new()
   {
      var d = DateTime.Now;
      T da = new T();
      int max = 10000000;
      for (int i = 0; i < max; i++) {
         da = ops.Add(da, inc);
      }
      Console.WriteLine((DateTime.Now - d).TotalMilliseconds / max * 1000000);
   }

   public static void Main(string[] args)
   {
      test<DoubleArithmetic, double>(1.0);
      test2<double>(1.0, new Combiner<double>((a, b) => a + b));
   }
}
Wobei ich die Delegate-Variante im Nachhinein doch eleganter finde. Schneller ist sie auch, was mich nicht sehr wundert
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#7

Re: [.NET] Generics und Operatorüberladung

  Alt 10. Jul 2009, 21:43
Beide Varianten führen letztlich leider nicht zu dem, was ich mir so vorgestellt hatte. Mir ist es am wichtigsten, dass die Anwendung von Operatoren auf die Klasse Matrix am Ende so simpel ist wie auf primitive Datentypen. Der Grund ist, dass die Formeln die diese Matrizen enthalten an und für sich schon komplex genug sind, so dass formales "Rumgehampel" hier zu totaler Unübersichtlichkeit führen würde.

Ich möchte einfach "P = M*(A+B)" schreiben können, egal ob die Elemente der einzelnen Matrizen nun vom Typ double, int oder Matrix sind. Und für den Fall dass eine nicht lösbare Konstruktion auftritt (wenn z.B. Matrix+double auftaucht), würde ich das am liebsten via Exception aus dem entsprechenden Operator heraus abfangen. Insbesondere die Version mit dem Interface (die mir an sich gut gefiel) hat das Problem, dass Interfaces keine Operatoren vorschreiben können
Das erklärt nun auch, warum es nicht schon längst ein IArithmetic Interface gibt.

Ich fürchte ich werde es ganz hässlich lösen müssen: Da maximal eine Matrix von Matrizen von Doubles auftauchen kann (keine tiefere Verschachtelung), werde ich wohl doch 2 Matrix-Klassen nehmen. Eine für double, eine für Matrix. Das wird nur etwas interessanter bei den Operatoren, die dann eine Fülle an Kombinationen von Parametern bedenken müssen.
Es sei denn es lässt sich nicht doch eleganter lösen!

Danke euch beiden schon mal kräftig!
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Dax
(Gast)

n/a Beiträge
 
#8

Re: [.NET] Generics und Operatorüberladung

  Alt 10. Jul 2009, 21:47
Wenn du dir eine MatrixFactory<T> definierst, die einen Combiner<T> als Konstruktorargument verlangt und eine Methode hat, die Matrizen erstellt und denen auch gleich den Combiner mitgibt, kannst du wunderbar Operatoren auf die Matrizen und deren Elementtypen definieren
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#9

Re: [.NET] Generics und Operatorüberladung

  Alt 10. Jul 2009, 21:57
Ooookay, klingt toll, aber bis ich das verstanden und umgesetzt habe dürfte es .NET 10 geben . Das ärgerliche ist für mich grad, dass das der aller erste Schritt von zig anderen nicht minder komplexen Dingen - ganz im Gegenteil - ist die vor mir liegen, und dass nur um mal was auszuprobieren. Ich dachte mir halt: Okay, fängste mit was einfachem an, ne Matrixklasse ist ja fix runtergetippert. Ich bin schon etwas demoralisiert nun =] Mich erwarten noch ganz andere Brocken. Man hätte mir sagen sollen, dass Spline-Patches an und für sich schon nichts für ein gemütliches Wochenende sind, und die sind nur die absolute Basis des Ganzen *soifz*.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Dax
(Gast)

n/a Beiträge
 
#10

Re: [.NET] Generics und Operatorüberladung

  Alt 11. Jul 2009, 14:05
Wieso denn, das geht doch ganz fix

Code:
class Combiner<T>
{
   public Func<T, T, T> Add {
      get;
      private set;
   }

   public Combiner(Func<T, T, T> _add)
   {
      Add = _add;
   }
}

class MatrixFactory<T>
{
   Combiner<T> combiner;

   public MatrixFactory(Combiner<T> combiner)
   {
      this.combiner = combiner;
   }

   public Matrix<T> New(int dimX, int dimY)
   {
      return new Matrix<T>(combiner, dimX, dimY);
   }
}

class Matrix<T>
{
   Combiner<T> ops;
   T[] values;
   int dimX, dimY;

   public Matrix(Combiner<T> ops, int dimX, int dimY)
   {
      this.ops = ops;
      this.dimX = dimX;
      this.dimY = dimY;
      this.values = new T[dimX * dimY];
   }

   public T this[int x, int y]
   {
      get { return values[(y - 1) * dimY + (x - 1)]; }
      set { values[(y - 1) * dimY + (x - 1)] = value; }
   }

   public override string ToString()
   {
      string result = "";
      for (int y = 1; y <= dimY; y++) {
         if (y > 0)
            result += "\n";
         for (int x = 1; x <= dimX; x++) {
            if (x > 0)
               result += "\t";
            result += this[x, y].ToString();
         }
      }
      return result;
   }

   public static Matrix<T> operator +(T left, Matrix<T> right)
   {
      Matrix<T> result = new Matrix<T>(right.ops, right.dimX, right.dimY);
      result.values = new T[right.values.Length];
      for (int i = 0; i < result.values.Length; i++) {
         result.values[i] = right.ops.Add(left, right.values[i]);
      }
      return result;
   }

   public static Matrix<T> operator +(Matrix<T> left, Matrix<T> right)
   {
      Matrix<T> result = new Matrix<T>(right.ops, right.dimX, right.dimY);
      result.values = new T[right.values.Length];
      for (int i = 0; i < result.values.Length; i++) {
         result.values[i] = right.ops.Add(left.values[i], right.values[i]);
      }
      return result;
   }
}

public class MainClass
{
   public static void Main(string[] args)
   {
      MatrixFactory<double> mf = new MatrixFactory<double>(new Combiner<double>((a, b) => a + b));
      Matrix<double> m = mf.New(4, 4);
      Matrix<double> m2 = mf.New(4, 4);
      Random r = new Random();
      for (int x = 1; x <= 4; x++)
         for (int y = 1; y <= 4; y++)
            m2[x, y] = r.NextDouble();
      m = 1 + m2 + m;
      Console.WriteLine(m);
   }
}
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 3  1 23      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:59 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz