Laden...
U
Benutzerbeschreibung

Forenbeiträge von Uwe81 Ingesamt 282 Beiträge

19.01.2010 - 00:02 Uhr

Dann mach die aber bitte noch const, sonst fliegen die als variablen im Speicher rum und können auch noch im Code verändert werden...

Dann steht irgendwo in einer Klasse


Binary.b00000001 = 8;

DEN Fehler will ich nicht suchen müssen 😉

18.01.2010 - 13:21 Uhr

Hallo!

Ich suche in Buch zum Design komplexer GUI-Anwendungen.

OOp habe ich verinnerlicht, auch viele Patterns verwende ich intuitiv, Clean Code nehme ich als Anleitung. Also kann ich schon etwas programmieren.

Aber bei größeren GUI-Anwendungen hab ich immer wieder architektonische Probleme. Das eigentliche Problem ist die komplexe State-Machine der GUI. Also "Wenn keine Datei geöffnet, dann sind die Buttons X, Y und Z disabled". "Wenn eine Datei geöffent, dann ist X und Y enabled, Z aber noch nicht". "Wenn mehrere Datein geöffnet, dann ist Z enabled, aber X nicht mehr".

Wie bildet man sowas ab? Irgendwie fehlt mir da Kompetenz. Gibt es dazu Literatur?

Vielen Dank,
Uwe

PS: Bisher nutzen wir für die GUI nur Windows Forms.

17.01.2010 - 23:00 Uhr

Ich erstelle dienstlich meine Software (wenn ein-bis-zwei-Mann-Projekt) eigentlich gerne so, dass schnell mal die Software läuft. Also werden erstmal alle Schichten implementiert, vernünftig getrennt, aber eben nur ein Teil. Dann wird alles der Reihe nach erweitert. Dadurch sieht man schnell, wie die Software aussieht und was sie machen soll.

Das liegt aber daran, dass wir (Forschungs-Institut) absolute Non-Standard Software, meist sogar nur Prototypen, entwickeln und da i.d.R. ein Lasten- oder gar Pflichtenheft (wenn vorhanden) kein Meeting übersteht. Wenn wir anfangen zu entwickeln wissen wir nicht wirklich, was der Kunde will, und der Kunde garnicht, was wir eigentlich liefern können.
In so einem Umfeld macht es keinen Sinn, monatelang das Modell und die Business-Logik fertigzustellen und dann zu erfahren, dass der Kunde was anderes wollte.

Wenn ich privat was entwickel (Promotion oder zu Hause zum Spaß) dann ist die GUI meist das Letzte. Dann ist mir eigentlich klar, was ich will, und ich implementiere erstmal das Modell.

Insofern ist das ziemlich abhängig von dem Entwicklungsumfeld.

17.01.2010 - 13:59 Uhr

Eine erste Anlaufstelle (die aber schon ins Detail geht) sind die ersten 5 Kapitel von
Galileo Openbook: Objektorientierte Programmierung. Besonders Kapitel 3 geht auf diese Grundanliegen ein. Die restlichen Kapitel ab 6. werden dann schnell technischer.

17.01.2010 - 11:36 Uhr

Habe jetzt gesehen das Type.GetHashCode() überschrieben ist. (Und auch genauso schnell wie referenz).

Das ist auch nicht besonders verwunderlich, weil die Indizierung per Referenz nichts anderes macht, als GetHashCode aufzurufen.

Insofern ist die Methode mit "Type" als Key schon die (IMHO einzig) richtige Wahl. Wenn du nur nach GetHashCode indizierst, dann musst du dich selbst um Hash-Kollisionen kümmern. Aber am Ende ist das genau das, was ein Dictionary eben von Hause aus relativ effizient tut.

17.01.2010 - 11:32 Uhr

also wenn ich dich richtig verstehe, soll ich eine Klassenbibliothek für die Klasse "Auto" nehmen und eine Klassenbibliothek für die Form, in der man das Auto erstellen kann???

Ja, dass ist wichtig. Die Anzeige und das Modell müssen voneinander getrennt werden.
Das sind unterschiedliche Aufgabenbereiche. So kann das Modell z.B. auch sinnvoll verwendet werden, wenn es keine GUI gibt. Stell dir vor, du liest aus einer Datei Operationen ein und führst diese auf dem Modell aus. Oder du schreibst eine Konsolen-Anwendung. Oder das ganze soll ein Web-Frontend bekommen.

Das ist für die meisten Mini-Anwendungen weit hergeholt, da wird es immer nur die eine Oberfläche geben. Aber ein wesentlicher Sinn von OOP ist die Trennung der Verantwortlichkeiten. Das erhöht nicht nur die Wiederverwendbarkeit sondern auch die Verständlichkeit.
Und graphische Darstellung und Modell sind unterschiedliche Verantwortlichkeiten, sollten daher voneinander getrennt werden,

Viele Grüße,
Uwe

15.01.2010 - 15:06 Uhr

Wichtig ist aber, dass der Overhead eines Arrays konstant ist.

Ein int sind 4 byte am Stack.

Ein int[1] sind (minimum) 4 byte am Stack für den Pointer, dann nochmal (minimum) 8 byte am Heap (4 für die Länge, 4 für den Wert).
Overhead sind also die 8 byte (4 für den Pointer, 4 für die Länge).

Wenn du nun ein Array der Länge 1000 anlegst, hast du immernoch nur diese 8 byte an Overhead. Je länger dein Array wird, um so mehr kannst du den overhead also Vernachlässigen.

Viele Grüße,
Uwe

09.01.2010 - 11:16 Uhr

ist nun off-topic

nichts für ungut aber warum übergibst du die id eines objektes und nicht gleich das objekt selbst? dann hast du es gleich typisiert und die id ist die referenz

Zum einen erwarten mathematische Algorithmen sehr oft nummerierte Elemente. Fast alle Graphenalgorithmen sind wesentliche einfacher, wenn die Knoten 0...n-1 und die Kanten 0...m-1 sind, die Indizierung ist also notwendig. Mixed Integer Programme sind sonst völlig unmöglich. Insbesondere ist die lokale Indizierung der Personen in einem Department notwendig, wenn ich einen Algorithmus nur auf diese Teilmenge anwenden möchte.
Das alles immer neu zu indizieren oder zwischen Objekten und IDs hin und herzuwechseln is teuer und fehleranfällig.

Zum anderen trat bei uns das Problem auf (das kann aber auch eine softwarearchitektonische Schwäche bei mir sein), dass der Code dann untestbar wurde. Die Objekte im realen Beispiel enthielten viele verschiedene technische Eigenschaften, von denen ein Algorithmus ich i.d.R. nur wenige brauche. Oder wissen muss, ob objekte unterscheidbar sind.

Oftmals reicht es in unseren Algorithmen zu wissen, dass ich wohl unterscheidbare Objekte habe. Ich muss nur wissen, ob Personen gleich sind oder nicht. Die ID reicht. Solche Testdaten lassen sich leicht aufsetzen. Habe ich hingegen nicht eine ID sondern eine Referenz, dann müssen die Personen initialisiert sein. Ihnen muss also ein Alter, Vorname, Nachname, Besoldungsstufe, ... zugewiesen worden sein.

08.01.2010 - 18:24 Uhr

Ist zwar schon älter, aber: Typisierte Integers!

Was soll das sein: Wir verwenden für die Implementierung diskreter Algorithmen C#. Da kommen häufig indizierte Objekte vor. Die dann auch noch Redundant gespeichert werden müssen.

Also habe ich z.B:


class Person{
    int id;
    int department;
}

class Department{
    int id;
    int[] persons;
    int[] boss;
}


class Specification{
    Person[] persons;
    Department[] departments;
}

Aus performancegründen führt an Arrays kein weg Vorbei, Dictionaries sind keine Alternative.

Nun ist das Problem, das ich z.B. mit der Department-ID auf eine Person zugreifen kann. Also departments[person.id]. Das bekommt man i.d.R. noch gut hin. Ekelhaft ist eher die Verwechselung lokaler und globaler IDs (ist z.B. boss in Department der Index der Person im Department oder global?)

Mit typesicheren Arrays und IDs ginge das ganze viel einfacher:

Ist zwar schon älter, aber: Typisierte Integers!

Was soll das sein: Wir verwenden für die Implementierung diskreter Algorithmen C#. Da kommen häufig indizierte Objekte vor. Die dann auch noch Redundant gespeichert werden müssen.

Also habe ich z.B:


define PersonId:int;
define DepartmentId:int;

class Person{
    PersonId id;
    DepartmentId department;
}

class Department{
    define LocalPersonId:int;
    DepartmentId id;
    PersonId[<LocalPersonId>] persons;
    LocalPersonId[<LocalPersonId>] boss;
}


class Specification{
    Person[<PersonId>] persons;
    Department[<DepartmentId>] departments;
}

Dabei stellt ein Array Person[<PersonId>] sicher, dass als Index nur eine PersonId übergeben werden kann.

In C++ kann man sich das mit Structs und nachbauen, das wird alles wegoptimiert. In C# geht das leider nicht, da kostet sowas zu viel Performance. Obiges Konstrukt hingegen würde zur Copilezeit geprüft werden können und kostet zur Laufzeit keine Performance.

Ähnlich sind die Units in F#.

Viele Grüße,
Uwe

19.12.2009 - 23:42 Uhr

Kann man eigentlich herausfinden welche funktionen einer dll importiert werden können ?

Z.B. mit DLL Export Viewer

19.12.2009 - 23:33 Uhr

Das kann z.B. mbUnit (ist integiert in Gallio)


public IEnumerable<IMath> MathInstances() {
    yield return new MathA();
    yield return new MathB();
}

[Test]
[Factory("MathInstances")]
public void Test_MathAdd(IMath math) {
    Assert.AreEqual(5, math.Add(2,3));
}

Man kann damit auch z.B. Instanzen vom selben Typ mit unterschiedlichen Initialisierungen testen.

IMHO eines der Killer-Features von mbUnit.

19.12.2009 - 23:12 Uhr

Ist zwar schon ein wenig älter, will aber auch mal meinen Senf dazu geben.

Ich finde, das über 95% der Kommentare überflüssig sind.
XML-Kommentare finde ich als Nutzer einer API nur dann sinnvoll, wenn sie wirklich ausführlich geschrieben sind - was sind die Annahmen, was die Auswirkungen der Methode, welche Exceptions können geworfen werden. Meistens ist das aber offensichtlich.
Wenn ein Kommentar beschreiben muss, was eine Methode tut, ist die Methode falsch benannt.

Innerhalb des Codes sind nur sehr wenige Kommentare notwendig. Lieber mehr und sprechendere Methoden. Ich halte mich da recht Konsequent an "Clean Code" und finde seit dem auch alten Code wirklich gut lesbar.

Z.B. finde ich folgenden Vorschlag ein schönes Gegenbeispiel für die Nützlichkeit von Kommentaren:


/// <summary>
  /// This class represents a customer and stores all necessary informations
  /// </summary>
  class Customer
    {
    /// <summary>
    /// Initializes a new instance of the <see cref="Customer"/> class and sets the name property.
    /// </summary>
    /// <param name="name">the nickname of the customer</param>
    public Customer (string name )
      {
      Name = name;
      }

    /// <summary>
    /// Gets the nickname of the customer.
    /// </summary>    
    public string Name { get; private set; }
    }

Die Klassenbeschreibung ist redundant. Natürlich repräsentiert die Klasse Customer einen Customer. Und natürlich speichert sie alle Daten dazu, dass ist die Aufgabe einer Klasse.... Es müsste dokumentiert werden, wenn sie es nicht tut. Noch besser würde man dann die Klasse anders benennen.

Dann werden die Beiden Kommentare dazu verwendet, zu beschreiben, welcher Name gemeint ist.... Warum nenne ich nicht das Property direkt so?

Ich würde es (abgesehen von der leidigen Notwendigkeit der API-Doku) folgendermaßen machen:


class Customer
{
    public string NickName { get; private set; }

    public Customer (string nickName ) {
        NickName = nickName;
    }  
}

Wenn zu 85% die Kommentare von GhostDoc übernommen werden: Warum kann nicht der Kommentar automatisch in die API-Doku generiert werden, ohne den Code aufzublähen.

Denn dann würden Kommentare im Code als wichtig erkannt werden, weil sie etwas dokumentieren, was nicht selbstverständlich oder trivial ist.

19.12.2009 - 18:31 Uhr

Hallo!

Das praktische Problem habe ich gelöst, indem ich diesen auf dem Solver-Framework basierenden Teil in ein Beispiel ausgelagert habe...

Für den Rest habe ich noch eine weitere Lösung gefunden, die allerdings "unschön" ist... Man kann mittels Assembly.LoadWithPartialName die DLL auch dann laden, wenn man nicht den gesamten Namen kennt. Das ist in meinem Fall unkritisch, weil ich die wenigen benötigten Methodenaufrufe wohl kaum ändern werden. Allerdings gibt es eine Warnung, dass das Depreceated ist, man nun Assembly.Load verwenden sollte (was allerdings den vollen Assembly-Namen benötigt).

Fazit: Konkretes praktisches Problem gelöst / umgangen, prinzipielles Problem besteht noch.

Viele Grüße,
Uwe

18.12.2009 - 10:44 Uhr

Hallo!

Ich habe (für den internen Gebrauch in unserer Abteilung) eine komplexe GUI-Komponente entwickelt. Diese liegt als Source in einem SVN-Repository, jeder, der es verwendet, compiliert es sich selbst.

Es gibt nun eine Erweiterung für diese GUI-Komponente, die verschiedene lineare- und Nicht-Lineare Optimierungsalgorithmen verwendet. Diese Erweiterung ist in einem eigenen Assembly.

Das beinhaltet Logik und außerdem eine Schnittstelle zu den verschiedenen Algorithmen. Die meisten sind native DLLs und mittels DLL-Import eingebunden.
Perfekt, denn so habe ich keine CompileTime-Dependency auf die Solver. Sind zur Laufzeit die DLLs da, gut. Ansonsten kommt ein Fehler.

Jetzt möchte ich aber auch einen Solver aus der Microsoft Solver Foundation verwenden. Das ist aber ein Managed Assembly. Ich kann die komplette Verwendung in eine Methode packen, daher existiert zur Laufzeit erst die Abhängigkeit, wenn diese Methode auch wirklich aufgerufen wird. Perfekt.

Dummerweise benötigt man aber zur Compile Zeit die Solver Foundation. Auch wenn über 90% der Nutzer diese Funktionalität nicht verwenden werden.

Ich hatte nun folgende Lösungsideen:

  1. Ich kompiliere eine eigene DLL, die genau diesen Aufruf kapselt, und stelle diese als Binary ins Repository. Auf diese DLL kann dann eine Compile-Time Dependency vorhanden sein.
    Schlecht ist, dass ich eine zusätzliche DLL brauche, die nur ein Assembly wrapped.

  2. Ich lade bei Methodenaufruf die DLL aus dem GAC per Reflection und mache die drei benötigten Methodenaufrufe ebenfalls per Reflection.
    Schlecht ist, dass ich beim Laden des Assemblies den vollen Namen angeben muss, also bei jedem Update auf eine neue Version (auch wenn sich relativ sicher nichts an den verwendeten Methoden ändern wird) einen String im Source ändern muss.

  3. Ich setze in alter C-Manier ein #ifdef-Block um den Code.
    Schlecht ist, dass zur Compile Zeit festgelegt wird, ob ich den Code jemals verwenden kann. Auch wenn die DLL am Ausführungsrechner später vorhanden ist.

Fazit: Ich suche eine Möglichkeit, eine solch Lose Kopplung zwischen zwei Managed DLLs wie zwischen Managed und Unmanaged DLL mittels DLLImport hinzubekommen.

Vielen Dank für Tipps,
Uwe

06.08.2009 - 14:59 Uhr

nur mal so interessehalber? Wozu ist der Code überhaupt gut?

Naja, das ist ein Minimalbeispiel, dass das Problem demonstriert.

Zur Erklärung:
Ein C++ - Algorithmus schreibt numerische Ergebnisse in eine Textdatei. Dort kann es eben sein, dass ein Wert numeric_limits<double>::max() ist. Beim einlesen dieser Textdatei in C# kommt dann der Fehler.

Um zu schauen ob das ein Kompatibilitätsproblem ist, hab ich dann obigen Test gemacht, es tritt aber in C# selbst auch auf.

Das mit dem double.MaxValue.ToString("R"); ist ein guter Tipp, wenn es C#-Intern ist.

Jetzt muss ich nur noch suchen, wie ich das Format in C++ herstellen kann 😉

06.08.2009 - 14:26 Uhr

Nur so am Rande

double value = double.Parse(double.MaxValue.ToString());

Wirft eine Exception....

Bekomme das Problem zwar umgangen (TryParse und Stringvergleich), aber etwas nervig ist das schon.

Viele Grüße,
Uwe

03.08.2009 - 17:55 Uhr

Hallo!

Ich habe mittlerweile viel experimentiert und verblüffende Ergebnisse erhalten (Siehe unten)

Ergebnisse


NonVirtualEval: 146793
VirtualEval: 559995
InterfaceEval: 532066
CodeDomEval: 513142
Delegate NonVirtualEval: 524187
Delegate VirtualEval: 522908
Delegate CodeDomEval: 475496
Delegate Expression Tree: 514489

Fazit: Nichtvirtuelle Aufrufe sind deutlich am performantesten.
Muss man einen Virtuellen Aufruf in kauf nehmen, spielt es fast keine Rolle mehr, ob das per delegate oder nicht geschieht.

Verblüffend funde ich, das Expression Trees und mittels Code Dom kompilierter Code reproduzierbar schneller ist als zur Compilezeit generierter Code. Das macht sich umso mehr bemerkbar, je komplexer der Ausdruck ist, also scheint tatsächlich die Auswertung und nicht der Aufruf schneller zu sein.

Unten ist die Test-Datei.

Viele Grüße,
Uwe


using System;
using System.Diagnostics;
using System.Reflection;
using System.Linq.Expressions;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

public interface IFunction {
    double Eval(double x);
}

public class TestClass : IFunction {
    public double NonVirtualEval(double x) {
        return 3 + x + x * x;
    }

    public virtual double VirtualEval(double x) {
        return 3 + x + x * x;
    }

    public double Eval(double x) {
        return 3 + x + x * x;
    }
}

public class CodeDomAddCreator{
    static CodeStatement CreateReturnStatement() {
        return
            new CodeMethodReturnStatement(
                new CodeBinaryOperatorExpression(
                    new CodeBinaryOperatorExpression(
                        new CodePrimitiveExpression(3.0),
                        CodeBinaryOperatorType.Add,
                        new CodeArgumentReferenceExpression("x")
                    ),
                    CodeBinaryOperatorType.Add,
                    new CodeBinaryOperatorExpression(
                        new CodeArgumentReferenceExpression("x"),
                        CodeBinaryOperatorType.Add,
                        new CodeArgumentReferenceExpression("x")
                    )
                 )
            );
    }

    static CodeMemberMethod CreateMethodExpression() {
        CodeMemberMethod methodExpression = new CodeMemberMethod();
        methodExpression.Name = "Eval";
        methodExpression.ReturnType = new CodeTypeReference(typeof(double));
        methodExpression.Parameters.Add(new CodeParameterDeclarationExpression(typeof(double), "x"));
        methodExpression.Statements.Add(CreateReturnStatement());
        methodExpression.Attributes &= ~MemberAttributes.Private;
        methodExpression.Attributes |= MemberAttributes.Public;
        return methodExpression;
    }

    static CodeTypeDeclaration CreateType(CodeMemberMethod memberMethod) {
        CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration("AddCodeDom");
        typeDeclaration.IsClass = true;
        typeDeclaration.BaseTypes.Add(typeof(IFunction));
        typeDeclaration.Members.Add(memberMethod);
        return typeDeclaration;
    }

    static CodeCompileUnit CreateCompileUnit(CodeTypeDeclaration typeDeclaration) {
        CodeNamespace ns = new CodeNamespace();
        ns.Types.Add(typeDeclaration);

        CodeCompileUnit compileUnit = new CodeCompileUnit();
        compileUnit.Namespaces.Add(ns);

        return compileUnit;
    }

    public static IFunction Create() {
        CodeMemberMethod memberMethod = CreateMethodExpression();
        CodeTypeDeclaration typeDeclaration = CreateType(memberMethod);
        CodeCompileUnit compileUnit = CreateCompileUnit(typeDeclaration);

        CodeDomProvider provider = new CSharpCodeProvider();

        CompilerParameters parameters = new CompilerParameters();
        parameters.GenerateInMemory = true;
        parameters.IncludeDebugInformation = false;
        parameters.GenerateExecutable = false;
        parameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
        CompilerResults results = provider.CompileAssemblyFromDom(parameters, compileUnit);

        Assembly assembly = results.CompiledAssembly;
        Type functionType = assembly.GetType("AddCodeDom");
        return Activator.CreateInstance(functionType) as IFunction;
    }
}

public class ExpressionTreeCreator {
    public static Func<double, double> Create() {
        ParameterExpression param = Expression.Parameter(typeof(double), "x");
        Expression add =
            Expression.Add(
                Expression.Add(Expression.Constant(3.0), param),
                Expression.Multiply(param, param)
            );

        Func<double, double> func = Expression.Lambda<Func<double, double>>(add, param).Compile();
        return func;
    }
}

class Starter {
    const int size = 10000000;
    static Stopwatch sw = new Stopwatch();
    static double sum = 0;

    public static void Main() {
        TestClass testClass = new TestClass();
        IFunction testClassAsInterface = testClass;
        IFunction codeDomClass = CodeDomAddCreator.Create();

        Func<double, double> delegateToNonVirtualTestClassEval = testClass.NonVirtualEval;
        Func<double, double> delegateToVirtualTestClassEval = testClass.VirtualEval;
        Func<double, double> delegateToCodeDomEval = codeDomClass.Eval;
        Func<double, double> delegateToExpression = ExpressionTreeCreator.Create();


        sw.Reset(); sum = 0; sw.Start();
        for (int i = 0; i < size; i++) { sum += testClass.NonVirtualEval(0); }
        sw.Stop();
        Console.WriteLine("NonVirtualEval: " + sw.ElapsedTicks);

        sw.Reset(); sum = 0; sw.Start();
        for (int i = 0; i < size; i++) { sum += testClass.VirtualEval(0); }
        sw.Stop();
        Console.WriteLine("VirtualEval: " + sw.ElapsedTicks);

        sw.Reset(); sum = 0; sw.Start();
        for (int i = 0; i < size; i++) { sum += testClassAsInterface.Eval(0); }
        sw.Stop();
        Console.WriteLine("InterfaceEval: " + sw.ElapsedTicks);

        sw.Reset(); sum = 0; sw.Start();
        for (int i = 0; i < size; i++) { sum += codeDomClass.Eval(0); }
        sw.Stop();
        Console.WriteLine("CodeDomEval: " + sw.ElapsedTicks);



        sw.Reset(); sum = 0; sw.Start();
        for (int i = 0; i < size; i++) { sum += delegateToNonVirtualTestClassEval(0); }
        sw.Stop();
        Console.WriteLine("Delegate NonVirtualEval: " + sw.ElapsedTicks);

        sw.Reset(); sum = 0; sw.Start();
        for (int i = 0; i < size; i++) { sum += delegateToVirtualTestClassEval(0); }
        sw.Stop();
        Console.WriteLine("Delegate VirtualEval: " + sw.ElapsedTicks);

        sw.Reset(); sum = 0; sw.Start();
        for (int i = 0; i < size; i++) { sum += delegateToCodeDomEval(0); }
        sw.Stop();
        Console.WriteLine("Delegate CodeDomEval: " + sw.ElapsedTicks);

        sw.Reset(); sum = 0; sw.Start();
        for (int i = 0; i < size; i++) { sum += delegateToExpression(0); }
        sw.Stop();
        Console.WriteLine("Delegate Expression Tree: " + sw.ElapsedTicks);
    }
}

29.07.2009 - 10:23 Uhr

Die Werte kommen mir alle extrem (zu) schnell vor. Ich weiß nicht auf was für Hardware das getestet wurde, aber 150,000 Ticks für 10,000,000? Ich komme bei allen Funktionen auf wesentlich höhere Werte. Naja, egal...

Eigentlich ein Laptop von 2006 (genaue Daten gerade nicht vorhanden), aber nicht besonders schnell.

Kann es sein, dass du die Performance im Release-Build, ohne Compiler (CTRL+F5) getestet hast? [...] Wenn man aber "sum" nach der Rechenoperation noch prüft um ggf. weitere Aktionen auszuführen, sind "f2" und "f3" nur noch doppelt so schnell

Ja, so habe ich getestet. Aber dass das mit einer weiteren Prüfung langsamre wird liegt IMHO daran, dass natürlich insgesamt mehr Operationen durchgeführt werden, also der relative Anteil des virtuellen Aufrufs geringer ist.

Erstes Ergebnis zu CodeDom: Wenn ich mir Expressions zusammenbaue und die dann kompiliere komme ich an dieselbe Performance ran, wie bei einem vorher kompilierten Code ohne virtuelle aufrufe. Vermutlich werde ich das zumindest für die Elementaren Funktionen machen.

29.07.2009 - 00:00 Uhr

Hallo!

Sealed hilft leider nicht. Auch Generics brachten keine Performance.
Ich weiß nicht, wie ich um das Virtual rumkommen soll...

Aber nun spiele ich gerade mit CodeDom rum und versuche, mir CodeExpressions zu erzeugen.

Wenn ich etwas gefunden habe, werde ich es wieder posten.

Nur stellt sich die Frage ober der Aufwand der Manipulation mittels CodeDom nicht mehr Zeit braucht als der virtuelle Aufruf.

Naja, es wird um eine Funktion gehen, die etwa 100 geschachtelte virtuelle Aufrufe beinhaltet. Ich kompiliere das einmal und rufe es mehrere hundertausend mal auf. Das wird sich schon auszahlen.

Aber Performancetests kommen mit dem Beispiel 😉

Vielen Dank und viele Grüße,
Uwe

28.07.2009 - 22:31 Uhr

Hallo!

Ich stelle derzeit ein Template-Lastiges C++ Framework auf C# um. Es geht um Mathematische Funktionen, die im Framework algebraisch abgeleitet werden können.

Beispiel:


    interface IFunction {
        double Eval(double param);
        IFunction Derivative();
    }

    class Add : IFunction {
        readonly IFunction f1, f2;

        public Add(IFunction f1, IFunction f2) {
            this.f1 = f1;
            this.f2 = f2;
        }

        public double Eval(double param) {
            return f1.Eval(param) + f2.Eval(param);
        }

        public IFunction Derivative() {
            return new Add(f1.Derivative(), f2.Derivative());
        }
    }

In der Realität sind das auch Vektorwertige Funktionen und die Lesbarkeit durch operator-Überladung verbessert.

Das Problem: Die Auswertung einer Funktion enthält nun sehr viele virtuelle Methodenaufrufe.

Im Originalen C++ Framework ist das durch Templates und Compilezeit-Polymorphie gelöst worden. Die Klasse Add hätte also die beiden Unterfunktionen als Template-Parameter und kann somit die Methodenaufrufe zur Compilezeit inlinen. Das führt aber neben performaten Code zu unendlich langen Fehlermeldungen.

Nun entwickel ich einen Algorithmus, in dem eine Methode immer wieder aufgerufen werden muss. Immer dieselbe Methode für dasselbe Objekt. Und zur Compile-Zeit ist klar, welche virtuellen Methodenaufrufe gemacht werden müssen. Ist es möglich, dem Compiler klar zu machen, dass er diese Methodenaufruf optimieren und ggf. inlinen soll?

Falls das nicht zur Compilezeit möglich ist, kann man es evtl. zur Laufzeit machen? Mit CodeDom etc?

Um die Notwendigkeit zu verdeutlichen:


    class Constant1 {
        readonly double value;

        public Constant1(double value) {
            this.value = value;
        }

        public virtual double Eval(double param) {
            return value;
        }
    }


    class Add1 {
        readonly Constant1 f1, f2;

        public Add1(Constant1 f1, Constant1 f2) {
            this.f1 = f1;
            this.f2 = f2;
        }

        public double Eval(double param) {
            return f1.Eval(param) + f2.Eval(param);
        }
    }

    class Constant2 {
        readonly double value;

        public Constant2(double value) {
            this.value = value;
        }

        public double Eval(double param) {
            return value;
        }
    }

    class Add2 {
        readonly Constant2 f1, f2;

        public Add2(Constant2 f1, Constant2 f2) {
            this.f1 = f1;
            this.f2 = f2;
        }

        public double Eval(double param) {
            return f1.Eval(param) + f2.Eval(param);
        }
    }

    class Starter {
        const int size = 10000000;
        static Stopwatch sw = new Stopwatch();
        static double sum = 0;

        public static void Main() {
            Add1 f1 = new Add1(new Constant1(2), new Constant1(3));
            Add2 f2 = new Add2(new Constant2(2), new Constant2(3));


            sw.Reset(); sum = 0; sw.Start();
            for (int i = 0; i < size; i++) { sum += f1.Eval(0); }
            sw.Stop();
            Console.WriteLine("f1: " + sw.ElapsedTicks);

            sw.Reset(); sum = 0; sw.Start();
            for (int i = 0; i < size; i++) { sum += f2.Eval(0); }
            sw.Stop();
            Console.WriteLine("f2: " + sw.ElapsedTicks);

            sw.Reset(); sum = 0; sw.Start();
            for (int i = 0; i < size; i++) { sum += 2 + 3; }
            sw.Stop();
            Console.WriteLine("f3: " + sw.ElapsedTicks);
        }
    }

Ausgabe


f1: 1065984
f2: 148522
f3: 148618

Durch den virtuellen Aufruf vervielfacht sich die Laufzeit.

Vielen Dank für Tipps,
Uwe

23.04.2009 - 15:35 Uhr

Hallo

@Peter
Vielen Dank für den Link, das ist ja in der Tat sogar in vielen Fällen Nützlich, an die ich bisher nichtmal gedacht habe. Leider verwenden wir kein R#, haben also kein Plugin. Ist jetzt Die Abwägung ob man lieber das bessere Framework oder die bessere Integration haben will (oder nicht doch mal R# kauft....)

@winSharp93
Cool, sowas habe ich in C# bisher vermisst. Wenn nun noch irgendwann Const-correctness kommt wäre es die "perfekte" Sprache (ich weiß, dass es sowas nicht gibt).

Leider arbeiten wir mit der Professional Edition, so dass das statische Type-Checken ausfällt, aber vielleicht kommt dass dan eine Version später in die Prof. Edition.
Und Solange gibt es zumindest den dynamischen Type Check.

Viele Grüße,
Uwe

20.04.2009 - 18:02 Uhr

Da verlangst du von den Interfaces mehr als sie sind. Dein Beispiel mit der Liste ist sicherlich nicht falsch, aber auch nicht richtig in meinen Augen. Nehmen wir ne Liste die nur eineindeutige Items zulässt und bei Add das Item nicht einfügt wenn es schon vorhanden ist. Was nun? Ist das Interface nun falsch implementiert da das Item nicht hinzugefügt wurde, obwohl die Methode doch Add heißt?

Wenn die Spezifikation fordert, dass das Item nach Add enthalten ist, ist das Interface ja richtig implementiert, weil das Item eben drin ist (war es ja schon vorher)
Wenn die Spezifikation fordert, dass nach nach Add der Wert von Count um 1 größer ist als vorher, erfüllt ein solche Klasse Set die Spezifikation nicht, sollte sie also auch nicht implementieren. Denn dann täte das Set nur so, als ob es eine Collection ist, ist es aber nicht.

Und was ist mit Methoden wie z.B. ein void Print() die irgend ein Objekt drucken soll. Also Methoden die das Objekt an sich nicht verändern sondern nur eine Funktionalität bieten die sich aber nicht direkt messen lässt. Die passen auch nicht in deine Sicht der Interfaces, bzw. in deinem Testwunsch.

Es ist wieder die Frage, was die Spezifikation der Methode enthält. Wenn sie keine Vor- und Nachbedingung hat kann ich die Methode problemlos in ein Interface packen und auch verwenden, nur Testen kann ich sie dann nicht weil sie nichts zusichert.

Ich halte mich da an http://openbook.galileocomputing.de/oop/oop_kapitel_05_001.htm

Eine Unterklasse erbt grundsätzlich die Spezifikation ihrer Oberklasse. Die Unterklasse übernimmt damit alle Verpflichtungen und Zusicherungen der Oberklasse. Alternativ wird auch der Begriff Vererbung von Schnittstellen benutzt. Verbung der Spezifikation drückt aber besser aus, dass eine Unterklasse die Verpflichtungen mit übernimmt, die sich aus der Spezifikation der Oberklasse ergeben. Es handelt sich eben nicht darum, einfach die Syntax einer Schnittstelle zu erben.

Aber sei's drum, die Diskussion wäre einen eigenen Thread wert und ist hier Off-Topic.

Du erstellst dir eine Umgebung, führst deine Implementierungen aus und testest deine Spezifikation gegen die Eigenschaften der Umgebung, fertig.

Ich glaube dazu verstehe ich noch zu wenig von Unit Tests.


    public interface ICalculator{
        int Add(int a, int b);
    }

    public class Calc1 : ICalculator{
        public int  Add(int a, int b) {
            return a+b;
        }
    }

    public class Calc2 : ICalculator{
        public int  Add(int a, int b) {
            return b+a;
        }
    }


    [TestClass]
    public class CalculatorTest {
        ICalculator calculator;

        [TestInitialize]
        public void MyTestInitialize() {
            calculator = new Calc1();
        }

        [TestMethod]
        public void TestAdd() {
            Assert.AreEqual(5, calculator.Add(2, 3));
        }
    }

Normalweweise wäre da jetzt aber nicht nur diese eine Methode, sondern 15 Testmethoden.

Ich verstehe vermutlich nicht, was du mit "Du erstellst dir eine Umgebung" meinst...

Vielen Danke,
Uwe

20.04.2009 - 16:49 Uhr

Was meinst du mit Spezifikationen des Interfaces abtesten? Ein Interface gibt ja nur vor welche Funktionen vorhanden sein müssen (dies wird ja praktisch schon beim kompilieren getestet weil man Interfaces immer komplett implementieren muss).

Für mich gibt ein Interface mehr als den Syntax vor, nämlich zusätzlich noch die Semantik einer Methode.

Z.B. für ein Klasse, die ICollection<T> implementiert.
Wenn ich die Methode Add(T item) aufrufe, erwarte ich, dass nachher das item auch in der Collection enthalten ist.
Wenn ich Clear() aufrufe, erwarte ich, dass anschließend Wert Count gleich 0 ist.
Genauso sollte ein Iterieren über alle Elemente gerade Count Elemente aufzählen.

Eigentlich enthält jede Methode eines Interfaces noch eine Precondition (was muss der Aufrufer erfüllen) und eine Postcondition (was ist nach dem Aufruf garantiert), die sich in C# leider nicht formal sondern nur in der Dokumentation spezifizieren lassen.

Eben diese Eigenschaften will ich über Unit Tests prüfen.

20.04.2009 - 16:21 Uhr

Hallo!

Ich habe ein Interface ITolleKlasse und zwei dieses Interface implementierende Klassen MeineTolleKlasseA und MeineTolleKlasseB.

Nun hab ich UnitTests, die die Spezifikation von ITolleKlasse abtesten. Diese möchte ich gerne für die Konkreten Klassen MeineTolleKlasseA und MeineTolleKlasseB ausführen (ich kenne alle zu testenden Klassen zur Compile-Zeit).

Geht sowas? Wenn ja, wie?

Ich verwende derzeit MSTest mit Visual Studio 2008 Professional.

Vielen Dank für Tipps,
Uwe

13.03.2009 - 12:02 Uhr

Ich verstehe nicht ganz warum du die Methoden, die nicht von AutoSelectIndex abhängen für verschiedenen AutoSelectIndex-Werte testen willst. Oder geht es dir darum zu schauen ob die Methoden tatsächlich nicht durch den Wert beeinflusst werden?

Genau darum geht es. Der interne Ablauf einiger dieser Methoden hängt von dem Wert ab, auch wenn das Endergebnis (im Hinblick auf die getesteten Ergebnisse) identisch ist.

Du packst den Test mit allen Methoden in eine abstrakte Klasse mit der abstrakten Property "AutoSelectIndex". Diese Property verwendest du wie gewohnt in den Tests. Dann überschreibt du die Klasse 2 mal und gibt in der Property einmal den Wert true, und einmal false zurück.

Jup, genau das hat funktioniert. Vielen Dank.
Eigentlich auch recht naheliegend, aber irgendwie bin ich im Kontext von UnitTesting nicht auf normale Polymorphie gekommen.

Danke sehr,
Uwe

13.03.2009 - 10:30 Uhr

Hallo!

Ich habe eine Klasse mit dem Namen DataMatrix, deren Verhalten stark von einem Wert boolschen Wert _AutoSelectIndex _abhängt.

Nun habe ich relativ viele MethodenTests. Die Methoden, deren gewünschtes Verhalten von AutoSelectIndex abhängen, muss ich sowieso getrennt testen.

Alle anderen Tests würde ich gerne für beide möglichen Werte von AutoSelectIndex ausführen.

Die DataMatrix wird in einer TestInitialize-Methode erzeugt.

Ist so etwas möglich?

Verwendetes Framework: MSTest mit VS 2008 Professional.

Vielen Dank,
Uwe

15.01.2009 - 22:27 Uhr

Und eine CheckedListbox in dein Control reinpacken?
Da kann man den Checkstate der Items doch ohne Graphics abfragen.

Das ist aber ein Gewürde, weil dort ein Item, welche Checkbox aktiviert wird, auch selektiert wird etc (darf bei mir nicht sein). Also muss ich auch das ganze Maus-Gehandle selbst machen und immernoch die Items selbst zeichnen etc.

Dann leg doch einfach ein Flag an, und frag in der Paint-Routine, ob dieses gesetzt ist. Wenn nein, dann einmalig die CheckBox-Renderer-Methode aufrufen und den Wert in einer Member-Variablen speichern (oder alternativ: direkt die Size auf > 0 abfragen).

Danke, genau diese Idee habe ich nun umgeseztzt. Finde ich den am wenigsten unschönen Workaround...

15.01.2009 - 16:11 Uhr

Mir ist Schon klar, dass ich in der PaintEventArgs ein Graphics.Objekt habe.

Aber ich muss in der OnMouseDown berechnen, ob eine CheckBox geklickt wurde oder der Text neben der CheckBox (einmal muss ich den CheckBox-Status ändern, das andere mal muss ich das Item selektieren).

Also
if (checkBoxBounds.Contains(MouseCursorPosition)) ...

Dafür brauche ich die Größe der CheckBox, denn der CheckBoxRenderer bekommt die linke obere Ecke und zeichnet sie in der Paint-Methode.

Nun wollte ich aber vermeiden, in jedem OnDrawItem ein
Size checkBoxSize = CheckBoxRender.GetGlyphSize (e.Graphics, myState)

zu haben, da ich davon ausgehe, dass sich die Größe einer CheckBox nicht ändert.

15.01.2009 - 10:34 Uhr

Hallo JunkyXL

Das mit dem GetPreferredSize() vorher ist eine einfache aber clevere Lösung, hätte ich auch selbst drauf kommen können...

Wobei mir parallel dazu einfällt, wieso du die CheckBox nicht in OnPaint zeichnest?!

Ich zeichne Sie in OnPaint, wollte nur vorher Berechnen wie groß sie ist (um das einer Klassenvariablen zuzuweisen, die ich in Eventhalndlern brauche). Und dafür braucht der CheckBoxRenderer ein Graphics-Object.

Grüße,
Uwe

14.01.2009 - 20:45 Uhr

Ok, vielleicht dann der etwas größere Kontext...

Ich bastel mir eine CheckedComboBox mit allen möglichem Kram, den im im speziellen Anwendungskontext brauche.

Idee:
Ein eigenes Controll für die TextBox und den Button (mit ComboBoxRenderer gezeichnet), dann packe ich eine ListBox in ein ToolStripDropDown und stelle das ggf. dar.

Nun brauche ich aber immernoch eine ListBox, die alle benötigten Features hat (man muss Items enabled und disablen können, die Items brauchen Farben, CheckBoxen sowieso etc).
Ein sauberes Ableiten der Klasse von ListBox bekomme ich nicht wirklich hin (der innere Aufbau der ListBox ist doch eher kompliziert), also nehme ich einfach ein Control, packe da eine ListBox rein und leite alle benötigten Aufrufe durch. Unter anderem kann ich dann in die ListBox Objekte reinpacken, die zu jedem Item die benötigten Daten beinhalten (Farbe, Enabled, Checked, ...)

Nun überschreibe ich das OnDrawItem Event um meine Items zu zeichnen.

Jetzt soll der Control bei GetPreferredSize die GetPreferredSize der ListBox zurückgeben, damit sich der ToolStripDropDown anpasst.

Das CreateGraphics() brauche ich eigentlich nur, um mittels CheckBoxRenderer die Größe einer CheckBox zu erhalten, damit ich die Klicks ausweten kann (Checkbox ja oder nein etc).

Grüße,
Uwe

14.01.2009 - 20:20 Uhr

Ich habe ein ParentControl, in dem mit DockStyl.Fill eine ListBox enthalten ist.

Nun gibt die ListBox mit GetPreferredSize() unterschiedliche Werte zurück, je nachdem ob im Contructor des ParentControl CreateGraphics aufgerufen habe oder nicht.


    class ParentControl : Control {
        ListBox lb;

        public ParentControl() {
            lb = new ListBox();
            lb.Items.AddRange(new object[] { "foo", "ba", "gfdgsfdhgsh", "x" });

            //using (Graphics g = CreateGraphics()) { }

            Controls.Add(lb);
            Console.WriteLine(lb.GetPreferredSize(Size.Empty));
        }
    }

So erhalte ich den Wert 120/96
Kommentiere ich die Zeile "using ..." aus, so erhalten ich 90/59

Woran liegt das?
Gibt es Abhilfe dafür?

Vielen Dank,
Uwe

12.01.2009 - 22:33 Uhr

Welche Methoden Properties brauchst du denn wirklich?

Value

Minimum
Maximum

TickFrequency
TickStyle

SmallChange
LargeChange

ValueChanged (event)

Das würde ich eher durchreichen (das Event weiterleiten ist das einzig nervige), ist nicht so aufwändig und die Kapselung ist sauberer.

Grüße,
Uwe

12.01.2009 - 22:02 Uhr

Wenn man nur eine Zahl ausgibt ist es vermultich so in Ordnung, aber ich habe im Anwendungsfall ein Intervall, aus dem mehrere Werte in verschiedenen Labels stehen. Und wenn nun Werte zwischen 0 und 10 ausgegeben werden, möchte ich halt 0.0000123 eben als 0.00001 und nicht als 1.23E-5 angeben. Mir wäre es lieber, eine einheitliche Schreibweise für alle Werte eines Intervalls zu haben.

Es reicht aber für die Anwendung. Eigentlich sollte ich auch an meiner Promotion arbeiten und nicht an einem GUI-Framework basteln. Da reichen auch mal 80%-Lösungen....

In einer von label abgeleiteten Klasse ist das sowieso gekapselt.

Grüße,
Uwe

12.01.2009 - 13:32 Uhr

Nach einigem Expertimentieren habe ich nun eine zufriedenstellende, wenn auch nicht perfekte Lösung gefunden:

              
//MaxNumberOfDigits ist die maximale stellenanzahl, die noch nicht exponentiell angezeigt werden soll.
for (int i = MaxNumberOfDigits; i >= 0; i--) {
    string str = numericValue.ToString("G" + i, formatProvider);
    int textWidth = TextRenderer.MeasureText(str, Font).Width;
    if (i == 0 ||  textWidth <= Width) {
        base.Text = str;  //Das ganze ist von Label abgeleitet, so das hier der Text gesetzt wird.
        break;
    }
}

Berücksichtigt zwar die Range etc. nicht aber für kleine Labelbreiten bis 7 oder 8 Stellen ist es ganz ok.

Fazit: Eine Lösung ist nicht nur deshalb schlecht, weil sie einfach ist....

12.01.2009 - 11:52 Uhr

* Du kannst mittels TextRenderer.MeasureString() eine Textlänge berechnen, die es beim Zeichnen benötigen würde. Das Ergebnis kannst Du mit dem tatsächlich verfügbaren Platz überprüfen und dann eine Fallunterscheidung machen, ob Du nun die verkürzte Schreibweise oder die komplette Darstellung anzeigst

Da bin ich derzeit dran, werde es hier einstellen, wenn ich es habe.

* Wenn es nur darum geht so viel anzuzeigen wie geht, ohne alles darstellen zu müssen, dann lass den Text doch einfach irgendwann abschneiden

Das geht bei der Wissenschaftlichen schreibweise nicht. denn 1.234 ist schon was ziemlich anderes als 1.234E200

Du könntest eine Schriftart nehmen, die pro Zeichen immer die selbe Größe benötigt (z.B. Courier new), dann kannst Du den verfügbaren Platz mit der Anzahl an Zeichen vergleichen, die man benötigen würde um die Zahl darzustellen

Das läuft im Prinzip auf das erste hinaus, wäre nur Performancetechnisch besser, da ich nicht andauernd MeasureString benötige. Da das aber nicht sooo performance-Kritisch ist, werde ich bei dem MeasureStrig bleiben.

Du könntest wirklich ein fertiges Control verwenden, wobei ich persönlich keines kenne

Schade.... Sowas hätte ich gesucht.

Mittlerweile habe ich aber noch festgestellt, dass Subjektiv die Darstellung nicht nur von dem Wert, sondern von der Range der darzustellenden Werte abhängt.

Liegen meine Zahlen zwischen 0 und 10, so werde ich 0.000001234 als 0.000001 darstellen. Liegen sie zwischen 0.000001 und 0.000002, so werde ich sie als 1.123E-6 anzeigen lassen.

Die Range ist in meinem Anwendungsfall zum Glück zur Laufzeit bekannt.

Bin dennoch weiter dankbar für Tipps,

Grüße,
Uwe

12.01.2009 - 10:47 Uhr

Hallo!

Ich habe ein Label vorgegebener Breite und möchte dort eine Zahl ausgeben. Das Problem ist, dass ich a priori keine Ahnung habe, in welchem Bereich die Zahl liegt. Es kann 10E-40 sein, genauso gut aber auch 10E+30

Nun möchte ich das irgendwie lesbar in das Label bekommen. Eine feste Anzahl von Nachkommastellen oder immer Exponentialdarstellung ist blöd, denn ich will da nicht "1.000" statt "1" oder "1.0E1" statt "10" drin stehen haben. Wenn ich den String nicht formatiere, kann es sein, dass eben "1.2345678E-13" drinsteht, dummerweise aber nur "1.123456" angezeigt wird.

Ich habe Ideen, wie man das umsetzen kann, aber das wird gebastel (im wesentlichen kann ich eine Darstellung nach der anderen von "Gefühlt schön" bis "Gefühlt hässlich" durchgehen und schauen, welche als erstes passt).
Und dann ist es immernoch subjektiv, wann man in die Exponentialdarstellung übergeht (ich würde 0.01234 noch zu "0.0123" abschneiden, aber 0.0001234 eher als "1.234E-4" statt "0.0001" darstellen.

Aber gibt es da nicht was fertiges? Ich werde ja nicht der erste sein, der dieses Problem hat...

Vielen Dank,
Uwe

11.01.2009 - 23:10 Uhr

Vielen Dank für die Antwort.

Ich habe mich nun auch dafür entschieden, einfach in die Doku die Reiheinfolge der Events zu schreiben.

Vermutlich läss sich bei verschiedenen, aber abhängigen Events das nicht immer anders lösen.

Bereitete mir an einer anderen Stelle zwar kopfzerbrechen, aber das lag daran dass ich mal wieder zu viel intelligenz in eine GUI-Komponente gepackt habe...

Grüße,
Uwe

09.01.2009 - 22:47 Uhr

Naja, aber dann würde ich immer nur alles auf einmal setzen können und das Grid kann nur ein allgemeines "ChangedEvent" werfen.

Dann muss aber jeder Listener - auch wenn ihn nur ein Teil interessiert - das allgemeine ChangedEvent abfangen und selbst rausfiltern, ob ihn die Änderung interssiert.

09.01.2009 - 22:32 Uhr

Hallo!

Ich habe ein Control (Grid), welches ein zweidimensionales Koordinatensystem darstellt. In diesem können Punkte dargestellt werden, jeder Punkt hat eine x, eine y Koordinate und eine Farbe.

Nun gibt es die events ValuesChanged und ColorsChanged;

Außerdem gibt es die Methoden


SetValuesX(double[] values){
    Setze X-Values
    OnValuesChanged();
}

SetValuesY(double[] values){
    Setze Y-Values
    OnValuesChanged();
}

SetColors(Color[] colors){
    Setze Farben
    OnColorsChanged();
}

Set(double[] valuesX, double[] valuesY, Color[] colors){
    Setze Werte und Farben
    OnValuesChanged();    
    OnColorsChanged();
}

Ich benötige die letze Methode, weil ich mit den ersten Methoden niemals die Anzahl der Punkte ändern kann. Sind bisher 5 Punkte im Grid und ich setze 10 Punkte für die x-Werte, so habe ich unterschiedlich viele x- und y-Werte, was ein ungültiger Zustand ist.

Das Problem ensteht nun in der letzten Methode.
In dem Moment, wenn ich das ValuesChanged Event werde, habe ich die Listener des ColorsChanged Events noch nicht benachrichtigt, der Listener geht also evtl. von einem anderen zustand des Grids aus als es tatsächlich hat.

Alternativ kann ich erst die Werte anpassen, dass das ColorsChanged Event werfen.


Set(double[] valuesX, double[] valuesY, Color[] colors){
    Setze Werte
    OnValuesChanged();    

    Setze Farben
    OnColorsChanged();
}

Dann wiederum ist zum Zeitpunkt, in dem das ValueChanged Event geworfen wird, das Grid in einem Intern ungültigen Zustand. Denn evtl. sind die Anzahl der Werte unterschiedlich von der Anzahl der Farben.

Wie löst man ein solches Problem?

Viele Dank,
Uwe

23.11.2008 - 11:45 Uhr

Hallo!

Ich schließe mich den Fragen meiner Vorredner an, evtl. musst du was an der struktur ändern.

Zu deinem eigentlichen Problem:


object o1 = 5; //int
object o2 = 4.23243; //double
//konvertiere o2 in int;
o2 = Convert.ChangeType(o2, o1.GetType());

Aber mit dem addieren klappt das leider dennoch nicht.

Blöderweiße helfen hier auch keine Generics weiter, weil man für die keine Operatoren fordern kann (Grrr.....).

Grüße,
Uwe

21.11.2008 - 00:53 Uhr

Ok, es wird Off-Topic. Bekenne mich schuldig 🙂

und gerade wenn es echter Zufall im Spiel wäre, würde der Algorithmus sicher terminieren. Denn die Wahrscheinlichkeit, dass ab einem bestimmten Punkt bis in alle Ewigkeit nur noch Zahlen kommen würden, die schon gewählt wurden, liegt bei exakt 0 (und nicht ein bisschen darüber). Sprich, dieser Fall kann nicht eintreten.

Naja, darum sagte ich ja

weil der Algorithmus nicht in endlicher Zeit sicher terminiert.

Denn für jede endliche Zahl "m" von Iterationen gibt es eine Wahrscheinlichkeit > 0, dass der Algorithmus nach m Iterationen noch nicht terminiert ist. Insbesondere hat er keine Laufzeitschranke O(f(n)).

Zwar stimmt es umgekehrt, dass zu jedem e>0 ein m existiert, so dass nach m Iterationen die Wahrscheinlichkeit für eine nicht-terminierung < e ist. Aber das ist eben nur stochastische Konvergenz.

Auf die Frage "Wie lange läuft das Programm maximal" kann man eben nicht sicher eine Antwort geben.

Denn die Wahrscheinlichkeit [...] liegt bei exakt 0. Sprich, dieser Fall kann nicht eintreten.

Damit hast du in diesem Fall recht, da wir einen diskreten Wahrscheinlichkeits-Raum haben. Im allgemeinen Stimmt es aber nicht, das "Wahrscheinlichkeit 0 == unmöglich" bedeutet.
Unter Normalverteilung hat z.B. jede reelle Zahl die Wahrscheinlichkeit 0, dennoch tritt bei Ziehen nach Normalverteilung ja sicher ein Ereigniss ein. Schlimmer noch: Sogar die Wahrscheinlichkeit, dass irgendeine rationale Zahl gewählt wird hat Wahscheinlichkeit 0. Es wird also mit wahrscheinlichkeit 1 eine irrationale Zahl gezogen. Aber eben nicht in jedem Fall.

Ok, war nun völlig Off-Topic.

20.11.2008 - 23:39 Uhr

Die Liste sollte man dann allerdings schon kopieren

Stimmt, habe ich übersehen. Oder man erzeugt sich ein int-Array der Länge n mit den Zahlen 0 bis n-1 und permutiert dieses. Braucht O(n) Speicher und Zeit.

Das mit dem Dictionary gefällt mir einfach nicht, weil der Algorithmus nicht in endlicher Zeit sicher terminiert. Das mag praktisch nicht relevant sein, aber ein Algorithmus der nur stochastisch terminiert, widerstrebt mir.

Grüße,
Uwe

PS: Vermutlich wird man, da die Zufallszahlen nur Pseudozufällig sind, doch irgendeine sichere Terminierung zeigen können, aber es bleibt ein fader Beigeschmack.

20.11.2008 - 23:20 Uhr

Diese Vorgehen ist dann besser als das von mir verklinkte Snippet, wenn man aus einer großen Menge von Zahlen nur relativ wenige wählen will.

Sehe ich nicht so. Ich würde k aus n Elementen wiefolgt wählen.


IEnumerable<T> Choose(IList<T> items, int k, Random rnd){
    int n = items.Count;
    for (int i = 0; i<k; i++) {
        int j = rnd.Next(i, n);
        Swap(items[i], items[j]);
        yield return items[i];
    }
}

void Swap<T>(ref T x, ref T y){
    T t = x;
    x = y;
    y = t;
}

Das entspricht im Wesentlichen deinem Snippet, aber du musst halt nicht nicht alles Shuffeln sondern wählst nur die bnötigten k Elemente zufällig.

Grüße,
Uwe

11.11.2008 - 18:39 Uhr

Hallo!

Danke für deine Antwort.
Platzprobleme gibt es mit der Init-Methode nicht. Sie wird vielleicht 20 bis 30 mal aufgerufen und braucht jeweils wenige KiloByte. Dennoch ist ein DeInit natürlich sauberer.

Das Synchronisieren der Aufrufe ist auch kein Problem, das wird ja von unserem C#-Code aus geschehen. Und ein Durchlauf von Init wird < 1s Brauchen (sollte aber nicht bei jedem der 100,000 Evaluate-Schritte passieren).

Dann muss ich das nur noch unserem Projektpartner beibringen, dass er intern für die Verwaltung paralleler Aufrufe zuständig ist.

Viele Grüße,
Uwe

11.11.2008 - 16:24 Uhr

Hallo!

Ich habe Technische/Konzeptionelle Frage.

Problem:
Wir haben einen Algorithmus und bekommen von einem Partner eine C++-DLL, in der zwei Methoden Init(.......) und Evaluate(......) bereitgestellt werden.

Die Init-Methode berechnet einige Daten vor, die Evaluate bewertet dann einen Bestimmten Datensatz.

Nun bleibt die Frage: Ist es sauberer, in der DLL die berechneten Werte vorzubehalten, oder sie zurückzugeben und bei jedem Evaluate wieder zu übergeben.

Wie sieht es aus, wenn ich auf Grund von Parallelisierung eigentlich mehrfach die Init-Methode aufrufen müsste?

Also


Init(Daten1)
Init(Daten2)
Evaluate(Datesatz für Daten1)
Evaluate(Datesatz für Daten1)
Evaluate(Datesatz für Daten2)
Evaluate(Datesatz für Daten1)
Evaluate(Datesatz für Daten2)
Evaluate(Datesatz für Daten2)
Evaluate(Datesatz für Daten1)

Wenn ich die vorberechneten Daten jedesmal mit übergebe, kein Problem. Aber ansonsten? Kann man eine DLL mehrfach "parallel" landen so dass sie in getrennten Speicherbereichen ihre Daten vorberechnen?
Oder sollte man einen Key bei der Init und der Evaluate-Methode mit übergeben, so dass bei jedem Datensatz die entsprechenden Vorberechneten Daten gewählt werden können.

Viele Grüße und vielen Dank,
Uwe

10.11.2008 - 21:33 Uhr

Leider habe ich nicht, wie in Java, Open Source bzw. Freeware finden können auf dem Gebiet.

Von NCover gibt es eine Freeware-Version. Zumindest für .NET 2.0 funktioniert die gut.

08.11.2008 - 22:05 Uhr

Du könntest z.B. in Geraet einen Konstructor implementieren, der als Parameter ein Gerät bekommt und sich die Daten kopiert.

Den müsstest du dann natürlich in Display etc. ebenfalls implementieren, dort aber nur an die Base-Klasse weiterleiten.

Aber eine andere Frage: Brauchst du überhaupt die ganzen abgeleiteten Klassen? Würde nicht evtl. ein enum auch reicht, dass du dann jeder Position zuordnen kannst?

Wo unterscheiden sich die von Geraet abgeleiteten Klassen?

07.11.2008 - 21:01 Uhr

Hallo!

Ja, deine Bauchschmerzen kann ich verstehe, ich teile sie sogar.

Mmmmh, ich glaube, ich muss mir über das ganze nochmal Gedanken machen.
So richtig gefällt mir auch der Equals nicht, denn ob zwei Kanten wirklich gleich sind, wenn ihre Endknoten gleich sind - sie aber unterschiedliche Länge haben - ist auch nicht völlig kanonisch.

Vielleicht lass ich das wirklich beides weg und stelle verschiedene IEqualityCompater und IComparer zur Verfügung.

Vielen Dank,
Uwe

07.11.2008 - 14:58 Uhr

Hallo!

Gibt es eigentlich einen notwendigen Zusammenhang zwischen CompareTo und Equals?

Konkret:
Ich habe einen Graphenalgorithmus, der ein struct "Edge" beinhaltet.


struct Edge {
    public int Node1 {get; set;}
    public int Node2 {get; set;}
    public double Length {get; set;}
}

Nun sind zwei Kanten genau dann gleich, wenn sie zwischen den gleichen Knoten sind. Das natürliche Sortieren hingegen erfolgt nach der Kantenlänge.

Somit


struct Edge : IComparable<Edge>{
    public int Node1 {get; set;}
    public int Node2 {get; set;}
    public double Length {get; set;}

    public override Equals(obj other){
        if (!(other is Edge)){
            return false;
        }
        Edge other = (Edge)obj;
        return (Node1 == other.Node1 && Node2 == other.Node2)
            || (Node1 == other.Node2 && Node2 == other.Node1);
    }

    public int CompareTo(Edge other) {
        return Length.CompareTo(other.Length);
    }
}

Das fürht allerdings dazu, dass sehr wohl CompareTo() den Wert 0 zurückgeben kann, obwohl Equals() false ist. Umgekehrt kann Equals() true sein, obwohl CompareTo() nicht 0 zurückgibt.

Ist das Nach Spezifikation ein Problem? In der Doku habe ich nichts gefunden.

Grüße,
Uwe

07.11.2008 - 00:44 Uhr

Das geht, wenn alle Klassen von einer gemeinsamen Basisklasse oder einem gemeinsamen Interface ableiten.


public interface IPosition {
    string Name { get; }
}

public class Drucker : IPosition {
    public string Name { get; set; }
}

public class Display: IPosition {
    public string Name { get; set; }
}

Dann kannst du alles in einer List<IPosition> speichern. Das Iterieren geht mittels


foreach (IPosition position in liste){
    TextBox.Text = position.Name;
}

Möchtest du aber wirklich nur den Namen haben, könntest du auch schlicht die ToString Methode überschreiben.