Laden...
U
Benutzerbeschreibung

Forenbeiträge von Uwe81 Ingesamt 282 Beiträge

01.06.2010 - 13:26 Uhr

Ich habe mal damit rumgespielt und finde es für manche Probleme intuitiv. Performance-Technisch meine ich mal gelesen zu haben, dass es i.d.R. etwas schlechter ist als C#. Dafür ist die Parallelisierung super simpel.

Es ist halt eine andere Art zu denken. Wenn man noch nie Funktional programmiert hat, wird es zu Beginn sogar abstoßend wirken.

Aber außerhalb von mathematischen Algorithmen halte ich C# für deutlich besser.

01.06.2010 - 08:36 Uhr

Eine Variante die ich gerne nutze ist, das ich für den Konstruktor ein extra Objekt übergebe, in dem die Parameter hängen.

Das wäre vielleicht eine Möglichkeit.

Nochmal etwas zu dem Hintergrund. Es geht eigentlich um einen Presenter zwischen einem Model (der GUI, keine Buissiness-Logik) und eigenen Controls. Das Model war bisher eine Gott-Klasse, die zwar intern an weitere Klassen delegierte aber nah außen ungefähr 10 Verantwortlichkeiten hatte. Diese habe ich aufgeteilt.

Nur braucht halt jeder Presenter etwa 3 bis 4 dieser Verantwortlichkeiten (nicht immer dieselben).

Vermutlich werde ich nun alle Klassen, die diese Verantwortlichkeiten haben, in einen Container packen. Dann gibt es in jedem Presenter zwei Konstruktoren: Einer, der wirklich alle Objekte einzeln übergeben bekommt (gut zum Testen und für spezielle Fälle) und einen, der den Container bekommt und sich selbst das Wesentliche rauszieht.

Viele Grüße,
Uwe

31.05.2010 - 21:58 Uhr

Wenn die Parameterlisten tatsächlich unerträglich lang werden, solltest Du ggf. Deine Architektur nochmal überdenken.

Danke für die Aufmunterung.... Diese Parameterlisten kommen aus einem Refactoring, weil ich vorher eigentlich eine Gott-Klasse mit 10 Zuständigkeiten hatte und jedes Objekt bekam die Gottklasse und ein oder zwei weitere Parameter.

Jetzt habe ich die Zuständigkeiten in einzelne Interfaces gepackt, aber nun braucht eben jedes Objekt zwei bis vier der zehn Interfaces plus ein oder zwei Parameter.

Also insgesamt 5 oder 6 Parameter.

31.05.2010 - 21:13 Uhr

Hallo!

Ich stolpere immer wieder über folgendes Problem:

Ein Objekt braucht, um sinnvoll arbeiten zu können, 6 andere Objekte von außen.

Was mache ich nun? Übergebe ich alle Objekte im Konstruktor? Dann habe ich eine laaaange Parameterliste.

Dürfen die Werte als Property gesetzt werden? Schöner bei der Initialisierung, aber man kann leicht was vergessen. Oder später ein Property nochmal setzen, was nicht wirklich sinnvoll sein muss.

Was ist da üblich?

Viele Grüße,
Uwe

31.05.2010 - 15:28 Uhr

Zu dem O(n) bei Dictionary: Die O-Notation berechnet ja den Worst-Case. Bei einer Hash-Table ist nun der einfachste Aufbau folgender: Es gibt ein Array mit verketteten Listen. Anhand des Hash-Werts werden nun die Einträge in die Listen einsortiert. wenn ich nun schauen will, ob ein Eintrag in einer Hashtable vorhanden ist, berechne ich den Hashwert und laufe dann linear durch die Liste an der Stelle im Array und vergleiche mit jedem Eintrag.
Werte, die in der selben Liste stehen, haben eine sogeanannte Hash-Kollision (sie sind nicht gleich, haben aber den selben Hashwert).

In der Praxis sind nun gute Hash-Funktionen so gestaltet, dass es wenige Hash-Kollisionen gibt. Ist das Array also groß genug und die Funktion gut genug, so werden die Listen immer relativ klein sein.

Allerdings gibt es die (bei ordentlicher Hashfunktion seeeehr unwahrscheinliche) Möglichkeit, das alle Elemente in der Hashtable denselben Hash-Wert haben, obwohl sie nicht gleich sind. Dann entartet die Hashtable zu einer verketteten Liste.

Es gibt IMHO keine Möglichkeit, dieses theoretische Problem zu umgehen, da die Anzahl der möglichen Objekte eben größer als die anzahl der möglichen Integerss (oder gar der Array-Größe) ist und somit die Hash-Funktion niemals injektiv sein kann.

Für die Praxis ist es aber irrelevant, wenn die Hash-Funktion ok ist.

Beispiel für dämliche Hash-Funktionen:
String -> Anzahl der Buchstaben (alle strings gleicher Länge haben eine Kollision)
Punkt -> Summe der Koordinaten (alle Punkte auf einer Diagonalen haben eine Kollision)

31.05.2010 - 12:53 Uhr

Ja, ich war da noch auf dem Dampfer, dass der OP vermutlich List verwendet und LinkedList denkt.

und so wie ich das sehe, ist hat hier das contains den größten performanceimpact. da wäre dictionary deutlichst perfomanter O(1)

Antwort als Praktiker: Stimmt, für Contains ist ein Dictionary in der tat besser, praktisch O(1)

Antwort als Mathematiker: Wieso, Contains für Dictionary ist auch O(n)...

31.05.2010 - 11:16 Uhr

Beachte, dass eine C#-List NICHT dieselbe Struktur wie in C++ eine stdlist ist.
In C++ ist eine list eine verkettete Liste, in C# ist es eher sowas wie in C++ ein std
vector. Vielleicht suchst du eher sowas wie eine LinkedList.
Dort ist Hinzufügen und Entfernen deutlich preiswerter (O(1)), wo hingegen bei einer List das O(n) ist (zumindest das entfernen oder hinzufügen am Anfang sogar immer, das Hinzufügen ans Ende ist IMHO im Schnitt besser).

Viele Grüße,
Uwe

27.05.2010 - 23:13 Uhr

Nur als Stichwort: Methode 2 und 3 erinnern mich an Signals aus Qt

24.05.2010 - 19:15 Uhr

Mag ja sein, dass ich schlecht programmiere oder Plane. Aber ich Refactor eigentlich andauernd.

Gerade bei Implementierung von Algorithmen überlege ich mir, wie ich das strukturiere und implementiere dann. Meistens finde ich aber nach der Implementierung, dass ein paar Methoden zu lang geworden sind, andere Klassen plötzlich doch ihren Aufgabenbereich etwas verschoben oder zwei Aufgaben übernommen haben. Dann gehe ich halt nochmal drüber und refactor.

Ist aber vielleicht auch eine Frage des Arbeitsstils. Auch wenn ich Texte verfasse schreibe ich erstmal ins Grobe den Inhalt und kümmer mich nachher um die Feinheiten, formuliere um, Zerteile Absätze etc. Also quasi Refactoring...
Andere (gerade ältere Semester, die ohne PC aufgewachsen sind) schreiben einen Text von oben nach unten runter.

Fazit: Wenn man die Pfadfinder-Regel nach CCD berücksichtigt (verlasse jeden Code immer ein Stück sauberer, als du ihn vorgefunden hast), refactored man andauernd. Wenn auch nur minimal.

Viele Grüße,
Uwe

24.05.2010 - 14:29 Uhr

private void button1_Click(object sender, EventArgs e) {
Test test = new Test();
test.Text1 = "Das ist ";
test.Text2 = "ein Test!";
}

public class Test {
public string Text1 { get; set; }
public string Text2 { get; set; }

public Test() {
EinString();
}

public void EinString() {
string gesamtText = Text1 + Text2;
Console.WriteLine("l: " + gesamtText);
}
}

Also: Innerhalb einer Klasse werden die Eigenschaften VOR dem Konstruktor erzeugt. Auch felder und ggf. deren initialisierung geschieht vor dem Konstruktor.

In deinem Fall passiert etwas anderes. Du erzeugst die ein neues Objekt und setzt dessen Eigenschaften anschließend von außerhalb. Die Ausgabe findet aber bereits im Konstruktor statt.

Also: Entweder rufst du die Methode EinString von außern auf (und nicht innerhalb des Konstruktors). Oder du musst die Werte für Text1 und Text2 als Parameter an den Konstruktor übergeben und sie vor dem Aufruf von EinString setzen.

20.05.2010 - 18:01 Uhr

Hatte ein solches PRoblem auch schonmal. Das ist im Typensystem von C# (und auch keiner anderen statisch typsisierten Sprache die ich kenne) möglich.

Es bleibt dir nur der Weg, einen der beiden Typen zu fordern und zur Laufzeit beim setzen zu checken, ob das objekt auch das andere Interface implementiert. Intern kann du dir den Cast ja hinter einem zweiten get-only property Verstecken oder es sogar gecastet in einer zweiten member-Variablen speichern.

Also z.B.


Interface1 myField1;
Interface2 myField1;

public Interface1 MyProp{
     get { return myField; }
     set {
        myField2 = (Interface2)value; //throws Exception on invalid cast
        myField1 = value;
     }
}

Ist nicht schön, geht aber nicht anders.

Eine weitere Möglichkeit wäre (wenn du es denn lieber magst) über Generics


public Interface1 MyProp1 { get; private set; }
public Interface2 MyProp2 { get; private set; }

public void SetValue<T>(T value) where T: Interface1, Interface2 {
    MyProp1 = value;
    MyProp2 = value;
}

Viele Grüße,
Uwe

20.05.2010 - 13:37 Uhr

Wenn das eine UI und das andere Core ist, dann darf die Klasse aus dem Core niemals das UI kennen.

Kannst du dass nicht über Events oder Interfaces lösen? Warum musst du bei einem Interface casten? Du kannst doch das Interface als solches verwenden, ohne die konkrete Implementierung zu kennen.

Fragen über Fragen,
Uwe

17.05.2010 - 11:14 Uhr

Generische Methoden können auch sinn machen, wenn der Typ-Parameter Eingeschränkt ist. Gerade bei zwei oder mehr Constraints lässt sich das sonst nicht wirklich formulieren.

15.05.2010 - 23:02 Uhr

Ich benötige öfters IDs, das sind eigentlich nur typsichere Strings. Also ein Struct, was intern einen String beinhaltet und zur Identifikation von Objekten dient.
Die will ich auch vergleichen oder als Keys in Dictionaries nehmen können.

Eine Standardimplementierung (hier für IDs für Dimensionen) ist


using System;

    [Serializable]
    public struct DimensionId {
        private readonly string value;

        public static bool operator !=(DimensionId id1, DimensionId id2) {
            return !(id1 == id2);
        }

        public static bool operator ==(DimensionId id1, DimensionId id2) {
            return id1.value == id2.value;
        }

        public DimensionId(string value) {
            this.value = value;
        }

        public override bool Equals(object obj) {
            return (obj is DimensionId) && (this == (DimensionId)obj);
        }

        public override int GetHashCode() {
            return value == null ? 0 : value.GetHashCode();
        }

        public override string ToString() {
            return value ?? string.Empty;
        }
    }

Beachte, dass Equals und der != Operator intern auf == zugreifen, um mögliche inkosistenzen zu vermeiden.

Viele Grüße,
Uwe

11.05.2010 - 10:58 Uhr

Mmmmh, ok.

Also ich würde um dein bisheriges Model einen eigenen ViewModel-Wrapper um das Model machen (extra für WPF).

Das finde ich eher unschön, weil man dann in WPF dennoch eine Referenz auf System.Drawing haben muss...

System.Drawing.Color ist IMHO zu unflexibel, um im Modell eingesetzt werden zu können.

Naja, es definiert eben eine Farbe. Eigentlich schreibe ich genau das Struct nun nochmal nach. Aber je mehr ich drüber nachdenke, umso hässlicher finde ich die Abhängigkeit auf System.Drawing.Color im Model auch...

Zumindest kann ich dann (wenn noch ein oder zwei "Size" aus dem ViewModel raus sind, die da aber eh nix verloren hatten) die Abhängigkeit auf Systen.Drawing entfernen.

Vielen Dank für die Tipps,
Uwe

10.05.2010 - 10:25 Uhr

Hallo!

Ich habe für In-House Anwendungen eine WinForms GUI-Komponente entwickelt, um Navigation in hochdimensionalen Lösungsräumen zu ermöglichen.

Grober Aufbau ist MVP, d.h in dem Fall konkret:*Es gibt ein recht komplexes, intelligentes Model (entspricht aber eher einem ViewModel, enthält also ein Model der GUI und nicht der eigentlichen Business-Daten) *Es gibt Standard und nicht-Standard WinForms-Controls. *Die beiden kennen sich nicht, dazwischen vermitteln Presenter, die auf Model-Events und Control-Events hören und die entsprechenden Aktionen im jeweils anderen auslösen.

Bisher gibt es in der Model-Schicht auch die Möglichkeit, Farbskalen zu definieren. Diese gaben System.Drawing.Color-Structs zurück.

Nun muss das ganze vermutlich in absehbarer Zeit auf WPF portiert werden (aber weiterhin auch mit WinForms einsetzbar bleiben). Quasi dasselbe Model aber pro GUI-Framework eigene Controls und Presenter. Was macht man da mit Colors? Ein eigenes Color-Struct definieren und in der Presenter-Schicht (die ja spezifisch pro Framework bleiben wird) konvertieren? Oder gibt es eine schönere Möglichkeit?

Vielen Dank,
Uwe

04.05.2010 - 15:44 Uhr

Ohne eine konkrete Antwort geben zu können:

Genetische (genauer: Evolutionäre) Algorithmen stehen und fallen mit der Qualität des Crossover-Operators. Ich weiß nicht, ob in deinem Fall ein sinnvoller Operator existiert.

Ansonsten kann auch eine nicht populationsbasierte Metaheuristik (z.B. Simulated Annealing) sinnvoll sein.

Viele Grüße,
Uwe

03.05.2010 - 10:00 Uhr

Ich denke, wenn du in deiner DA eine eigene Sprache entwickeln möchtest und die Uni das als mögliche DA akzeptieren, dann mach es einfach. Ich kenne auch die Regelung, dass in der DA nicht zwangsläufig was neues entstehen muss, sondern vielmehr, dass der Student zeigt, dass er wissenschaftlich arbeiten kann.

In 90% aller Fälle interessiert sich für die DA nach der Abgabe kein Mensch mehr.
Wichtig ist, dass du etwas lernst an einem Gebiet, was dir Spaß macht. Du darfst halt nur nicht erwarten, dass in 5 Jahren jeder nur noch "Tarsius#" implementiert.

Wobei ich es in der Tat interessanter fände, wenn die Sprache sich auf irgendeine Anwendung bezieht. Wenn es eine universelle Sprache ist, dann kannst du auch C# sonstwas nehmen und einfach die Vokabeln austauschen.

Schöner wäre es, wenn es einen Anwendungskontext gibt, den man mit bisherigen Sprachen nicht gut abdecken kann und du nun gerade diesen Teil gut machst (dann ist es auch nicht mehr relevant, ob deine Sprache in anderen Bereichen so gut ist wie existierende Sprachen -- denn das wird sie sicherlich nicht sein). Definiere dir also sehr früh EINE besondere Stärke, die deine Sprache haben soll, die andere nicht haben

So zeichnet sich z.B. F# daurch aus, dass man mit Einheiten rechnen kann. Ähnliches gibt es in C++/Boost schon, aber in F# wurde es in die Sprache übernommen und hat darduch einen viel schöneren Syntax.

23.04.2010 - 09:07 Uhr

Du berechnest

5000 * ln|(5000 + Sqrt(50002 - 0.12)) / 0.1| - Math.Sqrt(50002 - 0.12);

Die Zahlen haben große Unterschiede, 50002 und 0.12 Liegen um Größenordnungen 10^8 auseinander. Da wird es unweigerlich zu Rundungsfehlern kommen. Was willst du denn tun? Gibt es evtl. nuermisch stabilere Verfahren? Was meinst du mit "zumindest sind meine Ergebnisse falsch." Völlig falsch oder ungenau?

Ich habe aber das Gefühl, dass die Parameterform der Traktrix stabiler sein könnte.

22.04.2010 - 14:25 Uhr

dann sollte das feld der idatamatrix-property vom typ datamatrix sein... und du solltest dann mit dem feld arbeiten.

Geht in dem Fall nicht. Das Property ist in einer Basisklasse gesetzt und es kann vom TypSystem durchaus sein, dass es nicht vom typ DataMatrix ist. Ist ein recht flexibles Plugin-Konzept und dieses eine Plugin macht eben nur für DataMatrix (und keine andere IDataMatrix implementierende Klasse) sinn.

Unschön, aber meines Erachtens nicht anders lösbar.

22.04.2010 - 13:35 Uhr

[EDIT=herbivore]Abgeteilt von Coding Styles Horror[EDIT]

Gerade geschrieben:


DataMatrix dataMatrix = DataMatrix as DataMatrix;

Hintergrund: DataMatrix ist ein Property vom Typ IDataMatrix, ich brauche an dieser Stelle aber wirklich die DataMatrix...

Weiß nicht ob das schlecht ist, verwirrt aber erstmal beim Lesen.

15.04.2010 - 09:53 Uhr

SciLab kenne ich nicht, aber Octave ist IMHO nur für numerische Dinge brauchbar. Eben ein Matlab-Clone, was seine Stärken ja auch in der Numerik hat. Für Differentialgleichungen n-ter Ordnung wirst du aber i.d.R auch nur mit numerischen Verfahren weiterkommen.

Für symbolisches Rechnen gibt es übrigens noch Maxima. Dort ist der Unterschied zwischen Maple/Mathematica und Maxima aber schon weit. Für die meisten Hobby-Probleme hilft es aber schon.

Um es einfacher auszudrücken: Numerischen Lsg.-Verfahren sind Themen für Diplomarbeiten, Symbolische Verfahren für Doktorarbeiten.

Naja, ich mache in meiner Doktorarbeit weit mehr Numerik als in meiner Diplomarbeit. Es ist einfach eine Frage, was man bearbeitet. So kann man Polynome fünften Gerades analytisch (also auch symbolisch) schon nicht mehr lösen, numerisch ist das trivial. Oder eine Gleichung wie 2x=x2 kann analytisch nicht gelöset werden (man sieht x=2, x=4, aber x=-0.7666 ist eben analytisch nicht darstellbar)

Klar ist symbolisches Rechnen i.d.R. besser, wenn es denn geht. Aber bei den meisten Problemen geht es nicht. Dann geht es eher darum nachzuweisen, dass Voraussetzungen für die Konvergenz numerischer Verfahren erfüllt sind.

14.04.2010 - 15:25 Uhr

Finde Octave recht gut, weiß aber nicht, was es bei DGLs kann.
Siehe http://www.gnu.org/software/octave/.

26.03.2010 - 08:01 Uhr

Im moment bin ich immer noch in der Experimentierphase, wo ich auch durchaus mal falschen Code schreiben will,

Die habe ich hinter mir. Ich will keinen falschen Code schreiben, sondere mache das mittlerweile öfters ganz automatisch 😉

23.03.2010 - 18:02 Uhr

Idee: Ein enum von Noten (Werte bedeutungslos) machen und die Midi-Werte in einem Dictionary<Note, int> speichern. Nicht so performant wie enums direkt, aber evtl. "sauberer".

19.03.2010 - 09:08 Uhr

Ok!

Ich danke für eure Antworten und habe "IsDisabled" und "DisabledPointCount" rausgeworfen (sowohl aus der Klasse als auch aus dem Interface).

Wobei sich eine weitere Frage in einem Interface aufgetan hat (ich hoffe, ich kann das in diesem Thread anschließen, da es verwandt ist)

Die Klasse hat Properties


ICollection<DimensionId> Dimensions { get; }
int DimensionCount { get; }

Nun ist immer DimensionCount == Dimensions.Count.

Irgendwie ist das redundant. Aber nach Law of Demeter sollte man ja myObject.Dimensions.Count; nicht aufrufen. Was macht man da? DimensionCount in das Interface rein oder nicht? Oder aus den Dimensions ein IEnumerable machen?

Viele Grüße,
Uwe

18.03.2010 - 15:57 Uhr

Hallo!

Ich bin gerade dabei ein älteres Projekt zu Refactorn, u.a. einige Abhängigkeit zu Klassen durch die zu Interfaces zu ersetzen.

Nun komme ich an eine stelle, wo ich mir nicht sicher bin, was "schön" ist...

Eine Klasse verwaltet die Menge von Zuständen von "PointCount" Punkten (enabled oder disabled).

Ich habe eine Klasse, die bietet folgende Methoden


int PointCount { get; }
int EnabledPointCount { get; }
int DisabledPointCount { get; }

bool IsEnabled(int index);
bool IsDisabled(int index);

Das meiste ist natürlich redundant, es würde auch


int PointCount { get; }
bool IsEnabled(int index);

reichen.

Um EnabledPointCount nicht immer neu zu berechnen, sollte es aus Performance-Gründen mit aufgenommen werden.

Aber was ist mit IsDisabled? oder DisabledPointCount? Das lässt sich trivial errechnen. In einer Klasse finde ich es aus Convenience auch angenehm, IsDisabled liest sich einfacher als !IsEnabled. Aber in ein Interface? Das sollte ja schlank sein.

Andererseits schreibe ich dann schlechter lesbaren Code, um das Interface schlank zu halten.

Extension Methods stehen nicht zu Verfügung (.NET 2.0)

Was ist an der Stelle "schön"?

Vielen Dank,
Uwe

18.03.2010 - 11:09 Uhr

Meines Wissens gar nicht, du kannst nur fordern, dass es ein Struct ist.

15.03.2010 - 09:10 Uhr

Wenn das wirklich immer zweistellig ist, geht das mit "String.Substring" sehr einfach.

Problematisch wir das aber, wenn mal was nur einstellig angegeben wird. Wobei man da auch als mensch nicht weiß, ob 21110 nun der 21.01.2010 oder der 2.11.2010 ist.


string date = "120310";
string formattedDate = string.Format(
  "{0}/{1}/20{2}",
  date.Substring(0, 2),
  date.Substring(2, 2),
  date.Substring(4, 2));
Console.WriteLine(formattedDate);

EDIT: Code hinzugefügt

15.03.2010 - 08:25 Uhr

Als algorithmischer Hinweis:

Sowas nennt sich Pythagoreisches Tripel

Edit: Schreibfehler von "Phytagoräisches Tripel" korrigiert.

09.03.2010 - 15:46 Uhr

Danke für den Link, den hatte ich trotz googlens noch nicht gefunden.

Mmmmh, ok. In unserem Fall haben wir die auf der Seite vorgestellte Lösung verwendet. Ist dennoch irgendwie unschön. Schade, dass es nicht anders geht.

Viele Grüße,
Uwe

09.03.2010 - 13:11 Uhr

Hallo!

Wir haben eine C-DLL die mittels DllImport eingebunden wird. Allerdings gibt es in dieser C-DLL einen Parameter vom Typ double**.

Wie kann dieser übergeben werden? Ein einfacher double* wird ja als double[] übergeben. Ein double[][] funktioniert leider nicht. Braucht man für double** wirklich unsafe-Context?

Laut MSDN ist das Marshalling von nested Arrays nicht unterstützt.

Die Felhermeldung ist "Cannot marshal 'parameter #14': There is no marshaling support for nested arrays." (ja, ich weiß, die Parameterliste ist lang....)

Wir haben notfalls die Chance, das Array zu planarisieren (die inneren Arrays haben immer feste Länge), wäre aber etwas aufwändiger (die C-DLL stammt von Partnern).

Daher: Gibt es einen schönen Workarund? Ohne unsafe-Context.

Viele Grüße,
Uwe

09.03.2010 - 11:35 Uhr

Ist Kaffeesatzleserei, aber:

  1. Bei Algorithmen sind Arrays performanter als Listen
  2. Werden schnell große Speicherbereiche allokiert, kommt der GC nicht nach. Wir hatten schon den Fall, das ein manueller Aufruf von GC.Collect() die OutOfMemoryException behoben hat. Kostet aber Performance.
  3. Kannst du evtl. ein In-Place-Verfahren implementieren, d.h. du teilst die Listen nicht wirklich, sondern behältst sie in derselben Liste und merkst dir nur die bereiche? Ist oftmals fummeliger zu implementieren, aber i.d.R. schneller und weniger speicherhungrig (wenn es denn geht)
04.03.2010 - 16:06 Uhr

Jo das man nur erhöhen kann, war in Delphi damals auch so, dachte dass C# dies vielleicht erlaubt.

Das würde aber gegen das Liskov Substitution Principle verstoßen.

25.02.2010 - 09:46 Uhr

Das war nur ein sehr abstraktes Beispiel, ich möchte nicht über Linien o.ä. diskutieren sry :

Dennoch: Überleg, ob du wirklich Vererbung brauchst oder ob nicht Komposition, Strategy oder Decorator besser geeignet wären.

Fazit: Technich geht das, was du vorhast nicht. Es wirkt aber auch "komisch" und so, als ob ein anderes Software-Design das Problem beheben könnte. Ohne das konkrete Problem lässt sich das aber nicht sicher entscheiden.

24.02.2010 - 13:41 Uhr

Du ziehst eine Linie auf das Canvas (aus einer Palette z.b.). Anschliessend legst du fest, dass diese Linie gestrichelt ist. 'Gestrichelte Linie' ist eine Konkretisierung von 'Linie', d.h. das Objekt Linie wird zum Objekt Gestrichelte Linie. Gestrichelte Linien haben mehr Eigenschaften als Linien.

Ich bin mir nicht sicher, ob das nicht ein klassischer Fall von falscher Vererbung ist.
Eine oft hilfreiche Regel von OOP ist ja "favour aggregation over inheritance",

Ich würde vorschlagen: Jede Linie hat eine Property "Line Style", die "Dashed, Solid, ..." sein kann.

Stell die vor, du löst dass alles über Vererbung.

Dann hast du Klassen

Linie
GestrichelteLinie
SolideLinie
GestrichelteRoteLinie
GestrichelteGrüneLinie
SolideRoteLinie
SolideGrüneLinie
GestrichelteGrüneLinie1px
GestrichelteRoteLinie1px
GestrichelteGrüneLinie2px
GestrichelteRoteLinie2px
SolideGrüneLinie1px
SolideRoteLinie1px
SolideGrüneLinie2px
SolideRoteLinie2px

usw.

Das Explodiert. Stattdessen sollten die unterschiedlichen Eigenschaften in Poperties vorhanden sein.

LineStyle = Solid, Dashed, ...
Color = Red, Green, ...
Thickness = 1px, 2px, ...

Natürlich muss eine DashedLine noch zusätzliche Eigenschaften haben (wie weit sind die Striche, wie weit die Leerräume). Das würde ich über Polymorphie innerhalb der LineStyles lösen, d.h. die LineStyles sind Typen, die ein gemeinsames Interface implementieren.

Für das Zeichnen kannst du dann das Strategy-Pattern verwenden (an dieser Stelle muss dann der konkrete Type des LineStyles abgefragt werden).

Viele Grüße,
UWe

22.02.2010 - 10:58 Uhr

Vielen Dank für euer Feedback!

Ich habe mit die verlinkten Artikel durchgelesen und doch noch einigea an Hintergrundwissen zu der Thematik bekommen...

Intuitiv ist mir dieses Verhalten dennoch nicht. Was hat die Sprachdesigner dazu bewogen, keine Kopie der Value-Objekte zu erzeugen? Performance?

Viele Grüße,
Uwe

19.02.2010 - 15:17 Uhr

Hallo!

Ich habe ein merkwürdiges und unerwartetes Verhalten bei Lambda-Expressions festgestellt.


       const int count = 4;

        static ICollection<Func<string>> CreateFunctions1() {
            ICollection<Func<string>> functions = new LinkedList<Func<string>>();
            for (int i = 0; i < count; i++) {
                int j = i;
                functions.Add(() => j.ToString());
            }
            return functions;
        }

        static ICollection<Func<string>> CreateFunctions2() {
            ICollection<Func<string>> functions = new LinkedList<Func<string>>();
            for (int i = 0; i < count; i++) {
                functions.Add(() => i.ToString());
            }
            return functions;
        }

        static void Print(ICollection<Func<string>> functions) {
            Console.WriteLine(string.Join(", ",functions.Select(f=>f()).ToArray()));
        }

        static void Main(string[] args) {
            Print(CreateFunctions1()); //Liefert 0 1 2 3
            Print(CreateFunctions2()); //Liefert 4 4 4 4
        }

Das Ist im nachhinein erklärbar, da bei CreateFunctions1 eine Kopie j von i erzeugt wird und der Lambda-Ausdruck dieses j verwendet, bei CreateFunctions2 hingegen jeder Lambda-Ausdruck die Variable i verwendet und die eben nach der Schleife 4 ist.

Erwartet hätte ich aber bei einem Value-Type etwas anderes...

Gibt es eine Möglichkeit, das "schön" zu machen? Zumindest würde ich vermutlich beim nächsten Refactorn (in Abwesenheit eines Kommentars) das "j=i" wieder entfernen.

Viele Grüße,
Uwe

10.02.2010 - 13:55 Uhr

Versucht erstmal das Command-Pattern zu verstehen.

http://de.wikipedia.org/wiki/Kommando_(Entwurfsmuster)
http://en.wikipedia.org/wiki/Command_pattern

Die technische Umsetzung ist dann der zweite Schritt.

09.02.2010 - 16:03 Uhr

Arbeite an einem angewandten Forschungsinstitut.
Wir entwickeln gelegentlich selten Software für den Produktiveinsatz, oft aber auch nur Prototypen.

26.01.2010 - 13:19 Uhr

deine Überlegungen sind an sich schlüssig. Nur leider sind die Bezeichnungen nunmal genau umgekehrt festgelegt. Also das mathematische Runden ist das auf die gerade Endziffer. Und das kaufmännische Runden ist das, wo bei 0,5 immer aufgerundet wird.

Ok, ich sehe ein, dass es bei Wikipedia anders steht. Eine spontane Umfrage unter 5 (meist promovierten) Mathematikern mit unterschiedlichen Studienorten ergab, dass niemand das mathematische Runden als solches kannte 🤔. Zumindest scheint diese Definition also nicht allgemein Verbreitet zu sein.

25.01.2010 - 22:11 Uhr

Meines Wissens (bin immerhin Mathematiker) wird in der Mathematik eine Zahl genau dann auf die natürliche Zahl n gerundet, wenn sie im halboffenen Intervall [n-0.5, n+0.5) liegt. Das spielt auch praktisch für reelle Zahlen keine Rolle, weil Werte wie "exakt 7.5" selten vorkommen, u.A. nach jeder stetigen Verteilung mit Wahrscheinlichkeit 0 auftreten. Daher macht man sich wenig Gedanken, wie dort gerundet wird.

Im Kaufmännischen ist das ganze viel Wichtiger. Denn dort werden eben keine stetigen Verteilungen angenommen, sondern meist rechnet man mit auf Zahlen, due nach dem Komma zwei stellen habe (oder mehr, aber jedenfalls recht wenige). Wenn ich nun lauter Beträge habe, die auf zwei Stellen nach dem Komma genau sind, und diese mathematisch runde (also ab 7.50 auf 8 runden), dann erhöhe ich den Betrag im Schnitt um 0.005 Cent.

Anschaulich: Ich habe eine Liste mit 1000 Beträgen, die jemand mir Zahlen muss. Wenn ich nun alle diese Beträge auf Euro runde, sollte ich am Ende im Erwartungswert denselben Betrag erhalten (das wird natürlich zufällig streuen, aber es sollte keine systematische Tendenz in eine Richtung geben).

Bei kaufmännischem Runden (bei 0.5 auf die gerade Zahl) ist das so.
Bei mathematischem Runden (bei 0.5 aufrunden) würde ich im Erwartungswert jedesmal 0.005 Cent, in der Summe also 5 Euro gewinnen.

22.01.2010 - 00:16 Uhr

class Primes {
    static IEnumerable<int> DividerSequence() {
        yield return 2;
        yield return 3;       
        for(int i = 6; true; i+=6){
            yield return i - 1;
            yield return i + 1;
        }
    }

    public static ICollection<int> GetPrimeFactors(int number) {
        if (number <= 0) {
            throw new ArgumentException("Positive integer expected.", "number");
        }

        LinkedList<int> primeFactors = new LinkedList<int>();
        foreach (int factor in DividerSequence()) {
            while (number % factor == 0) {
                primeFactors.AddLast(factor);
                number /= factor;
            }
            if (factor * factor > number) {
                break;
            }
        }
        if (number > 1) {
            primeFactors.AddLast(number);
        }
        return primeFactors;
    }
}

class Program{
    static void Main(string[] args) {
        for (int number = 1; number < 100; number++) {
            ICollection<int> primeFactors = Primes.GetPrimeFactors(number);
            Console.WriteLine(
                "n={0}, Primes=[{1}]", 
                number,
                string.Join(", ", primeFactors.Select(x => x.ToString()).ToArray()));
        }
    }
}

Neue Aufgabe:
Man teste ohne Verwendung der Logarithmusfunktion mit einem einzigen Statement, ob eine Integer eine Zweierpotenz ist.

19.01.2010 - 17:34 Uhr

Oh je, jetzt sind meine IL-Kenntnisse überfragt. Ich meine, nach Definition müsse nur sichergestellt sein, dass die Bounds OK sind, nicht, dass wirklich zur Laufzeit ein Check durchgeführt wird. Aber meine Hand lege ich dafür nicht ins Feuer.

Habe noch folgenden Link gefunden.
http://blogs.msdn.com/clrcodegeneration/archive/2009/08/13/array-bounds-check-elimination-in-the-clr.aspx

19.01.2010 - 17:07 Uhr

Da scheint der Compiler es zur Compilezeit nicht zu erkennen.

Gegenbeispiel:


     class Programm {
        static int[] arr = new int[10];

        static void Main(string[] args) {            
            for (int i = 0; i < arr.Length; i++) {
                arr[i] = i;
            }
        }
    }


.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       27 (0x1b)
  .maxstack  3
  .locals init ([0] int32 i)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  br.s       IL_0010
  IL_0004:  ldsfld     int32[] Performance_Test.Programm::arr
  IL_0009:  ldloc.0
  IL_000a:  ldloc.0
  IL_000b:  stelem.i4
  IL_000c:  ldloc.0
  IL_000d:  ldc.i4.1
  IL_000e:  add
  IL_000f:  stloc.0
  IL_0010:  ldloc.0
  IL_0011:  ldsfld     int32[] Performance_Test.Programm::arr
  IL_0016:  ldlen
  IL_0017:  conv.i4
  IL_0018:  blt.s      IL_0004
  IL_001a:  ret
} // end of method Programm::Main

19.01.2010 - 16:46 Uhr

demnach dürfte sich die laufzeit im vergleich zu unsafe nicht ändern? probier das doch mal.

Hab das zu dem Test hinzugefügt (siehe oben, habe den Beitrag editiert). Es macht wie erwartet keinen Unterschied.

Ein Unterschied entsteht dann, wenn der Compiler nicht zur Compilezeit erkennen kann, dass der Zugriff korrekt ist, (eben weil es keine Laufvariable einer For-Schleife ist, sondern ein lokales Feld oder sowas). Dann wird er im unsafe Kontext keinen Check machen, im save Kontext hingegen schon.

Da der Compiler für eindimensionale Arrays optimiert, sind übrigens jagged Arrays auch deutlich performanter als mehrdimensionale Arrays

19.01.2010 - 16:28 Uhr

ein array in .net hat auch einen boundscheck. somit ist im safe-context ein buffer-overflow nicht möglich.

Der Compiler ist ja doof. Ich vielen Fällen kann und wird der Check wegoptimiert, z.B. bei einer For-Schleife von 0 bis array.length (daher sollte man sich nie die Länge eines Arrays als lokale Variable merken, das verdoppelt in meinem Code oben die Laufzeit). Dort ist ja zur Compilezeit klar, dass die Bounds immer erfüllt sein werden.

19.01.2010 - 16:14 Uhr

Arrays sind für Performance-Kritische Probleme nach wie vor am schnellsten.

nur im unsafe context. der rest ist vernachlässigbar und schadet meist der wartbarkeit.

Naja, auch sonst eben in obigem Beispiel einen Faktor 4. Der oft, aber eben nicht immer vernachlässigbar ist.

Der Unterschied kommt im Wesentlichen daher, dass bei einem Array a[4] nur ein Zugriff auf eine Speicherstelle ist, während bei einer Liste a[4] ein Methodenaufruf ist.

19.01.2010 - 16:03 Uhr

Es ist bei O(1) sogar denkbar, dass der Aufwand für einige (natürlich nur sehr wenige) Eingaben exponentiell ist.

Nein, das ist so falsch. Die O-Notation (siehe http://de.wikipedia.org/wiki/Landau-Symbole) ist eine obere Schranke und gibt die worst-case-Komplexität an.

19.01.2010 - 15:54 Uhr

Man sollte den Performance-Vorteil von Arrays nicht unterschätzen!
Arrays sind für Performance-Kritische Probleme nach wie vor am schnellsten.

Beispiel:

Ich führ Elementare Operationen auf Arrays und Listen aus.
Mal übergebe ich beides als konkreten Typ, mal als IList.
Mal kopiere ich die Länge in ein lokales Feld, mal frage ich sie jedesmal ab.


    class Program {
        const int count = 5000;
        static int[] arr = new int[count];
        static List<int> list = new List<int>();


        static void Main(string[] args) {
            Init();

            Console.WriteLine("int[] as int[]:A: " + MesaureTime(() => Sum1(arr as int[])).Ticks);
            Console.WriteLine("int[] as IList:A: " + MesaureTime(() => Sum1(arr as IList<int>)).Ticks);
            Console.WriteLine("List as List  :A: " + MesaureTime(() => Sum1(list as List<int>)).Ticks);
            Console.WriteLine("List as IList :A: " + MesaureTime(() => Sum1(list as IList<int>)).Ticks);

            Console.WriteLine("int[] as int[]:B: " + MesaureTime(() => Sum2(arr as int[])).Ticks);
            Console.WriteLine("int[] as IList:B: " + MesaureTime(() => Sum2(arr as IList<int>)).Ticks);
            Console.WriteLine("List as List  :B: " + MesaureTime(() => Sum2(list as List<int>)).Ticks);
            Console.WriteLine("List as IList :B: " + MesaureTime(() => Sum2(list as IList<int>)).Ticks);

            unsafe {
                fixed (int* ptr = arr) {
                    Console.WriteLine("int[] as int* :C: " + MesaureTime(() => Sum3(ptr, count)).Ticks);
                }
            }
        }

        static void Init() {
            for (int i = 0; i < count; i++) {
                arr[i] = i;
                list.Add(i);
            }
        }

        static TimeSpan MesaureTime(Action action) {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            action();
            sw.Stop();
            return sw.Elapsed;
        }


        static int Sum1(int[] a) {
            int sum = 0;
            for (int i = 0; i < a.Length; i++) {
                for (int j = 0; j < a.Length; j++) {
                    sum += a[i]\*a[j];
                }
            }
            return sum;
        }

        static int Sum1(IList<int> a) {
            int sum = 0;
            for (int i = 0; i < a.Count; i++) {
                for (int j = 0; j < a.Count; j++) {
                    sum += a[i] * a[j];
                }
            }
            return sum;
        }

        static int Sum1(List<int> a) {
            int sum = 0;
            for (int i = 0; i < a.Count; i++) {
                for (int j = 0; j < a.Count; j++) {
                    sum += a[i] * a[j];
                }
            }
            return sum;
        }

        static int Sum2(int[] a) {
            int sum = 0;
            int length = a.Length;
            for (int i = 0; i < length; i++) {
                for (int j = 0; j < length; j++) {
                    sum += a[i] * a[j];
                }
            }
            return sum;
        }

        static int Sum2(IList<int> a) {
            int sum = 0;
            int length = a.Count;
            for (int i = 0; i < length; i++) {
                for (int j = 0; j < length; j++) {
                    sum += a[i] * a[j];
                }
            }
            return sum;
        }

        static int Sum2(List<int> a) {
            int sum = 0;
            int length = a.Count;
            for (int i = 0; i < length; i++) {
                for (int j = 0; j < length; j++) {
                    sum += a[i] * a[j];
                }
            }
            return sum;
        }

        static unsafe int Sum3(int* a, int length) {
            int sum = 0;
            for (int i = 0; i < length; i++) {
                for (int j = 0; j < length; j++) {
                    sum += a[i] * a[j];
                }
            }
            return sum;            
        }
    }

Ausgabe (Release-Mode, kein Debugging):


int[] as int[]:A: 404179
int[] as IList:A: 37778960
List as List  :A: 1624798
List as IList :A: 7979447
int[] as int[]:B: 775808
int[] as IList:B: 23942406
List as List  :B: 1617253
List as IList :B: 6396561
int[] as int* :C: 400335

Fazit: Array sind (insbesondere wenn durch die Schleife bis "length" die Bereichsprüfung wegfällt) deutlich schneller als alles andere. Die Abstraktion als IList z.B. kostet einen Faktor 100. Auch eine List ist um einen Faktor 4 langsamer.

Das heißt nicht, dass nicht oft dieser Overhead vernachlässigbar ist. Aber er ist da.

EDIT: Unsafe Test hinzugfügt.