Laden...
B
Benutzerbeschreibung
Jabber-ID: bernhard@jabber.org

Forenbeiträge von Bernhard Ingesamt 119 Beiträge

17.04.2008 - 23:27 Uhr

Hallo,

da scheinst du etwas falsch verstanden zu haben, da das "protected" aus Java dem "protected internal" aus C# entspricht und zwar stimmt es, dass es kein direktes Äquivalent zum "protected" aus C# gibt, aber das ist meiner Meinung nach auch nicht weiter wichtig, da ein "protected internal" auch seinen Zweck erfüllt.

grüße,
Bernhard

10.06.2007 - 20:31 Uhr

Hallo,

Konstruktor-Verkettung lässt sich oft in zwei Richtungen begehen, in diesem Fall finde ich allerdings die andere Richtung angebrachter.


public class  A {
    private int feld1;
    private int feld2;

    public A() : this(100) {
        
    }

    public A(int feld1) {
        this.feld1 = feld1;
        this.feld2 = 1000;
    }
}

Jetzt kann man natürlich behaupten, dass dadurch die Default-Werte auf verschiedene Konstruktoren aufgeteilt werden (was ja auch stimmt) und somit die Übersicht verloren geht (was meiner Meinung nach wiederum nicht stimmt). Prinzipiell sind mir Default-Werte aber eigentlich auch relativ egal, da ich die Initialisierung einfach im Debugger verfolge, wenn ich das entsprechende Bedürfnis habe. Ansonsten führe ich auch unter Umständen öffentliche Konstanten ein, die die Default-Werte darstellen. Somit sind diese dann auch wirklich öffentlich zugänglich und müssen nicht, wenn sie denn notwendig sind, z.B. aus der Dokumentation entnommen werden.

Ansonsten bevorzuge ich eigentlich Variante #2, sofern es sich eben wirklich um Standardwerte handelt, soll heißen, dass die ansonsten in jedem Konstruktor genau so dastehen würden. Falls es eine Rolle spielt, in welcher Reihenfolge die Initialisierungen getätigt werden (z.B. wenn ein Feld als Parameter beim Konstruktor eines zweiten dient), bevorzuge ich normalerweise Variante #1 um eventuell auftretende Verwunderungen zu vermeiden.

grüße
Bernhard

26.01.2007 - 23:04 Uhr

Allerdings wird das (leider) im täglichen Sprachgebrauch nicht so sauber getrennt. So sind auch im .NET-Framework die deutliche Mehrzahl der Klasse zur reinen Benutzung und nur wenige rahmengebende, wie z.B. die WinForm-Klassen.

Wobei diese fehlende Trennung im Sprachgebrauch eigentlich ein Fehler ist, denn es heißt ja auch nicht umsonst Base Class Library.

grüße

13.01.2007 - 17:22 Uhr

Ich finde eigentlich nicht, dass es eine schlechte Idee ist nebenbei auch Java zu lernen. Zwar sind die Unterschiede zwischen C# und Java tatsächlich nicht so groß, wie zwischen zwei Sprachen, bei denen sich die Paradigmen unterscheiden, aber trotzdem noch groß genug um berechtigterweise zu behaupten, dass man nicht Java programmieren kann, nur weil man C# bereits kennt (und umgekehrt natürlich auch). Das fängt schon bei ganz banalen Dingen an, wie z.B: die Entscheidung ob eine Exception-Klasse, die man entwickelt, checked oder unchecked sein soll.

Lord Hessia schrieb:
Java unterscheidet sich von C# hauptsächlich darin, dass hinter C# ein viel größeres Framework steckt, während man bei Java viele Fremdbibliotheken benötigt. Erst wenn Du in den Bereich Webentwicklung gehst, gibt es große Unterschiede aufgrund der Vielzahl an Frameworks für die Webentwicklung mit Java.

Diese Vielfalt an Frameworks für die Webentwicklung in Java kommt wohl daher, dass Entscheidungen unter gewissen Umständen unterschiedlich getroffen werden. Je nach dem ob man bei der Webentwicklung auf das MVC Pattern schwört (oder daran gebunden ist anhand bestehender Legacy Systeme) oder lieber Komponenten entwickelt, wird man die Wahl des Frameworks anders gestalten. Weiters unterscheidet sich die Implementierung der Logik anders, je nachdem ob ein "einfacher" Servlet-Container oder ein vollwertiger Application Server zugrunde liegt. Dadurch kann ergibt sich auch ob einfache Integration von EJBs ein Kriterium für die Wahl des Frameworks ist. Aufgrund dieser und weiterer Kritieren finde ich es nicht schlecht, dass auch mehrere Frameworks zur Webentwicklung verfügbar sind.

grüße

24.12.2006 - 12:18 Uhr

Natürlich gibts in Java auch umfangreiche Programme die wirklich plattformunabhägig sind, bei denen fällt der Präsentationsteil mit der GUI aber tendenziell eher gering aus, und das bekommt man genauso mit .Net und MONO hin!

Hier muss man allerdings etwas differenzieren, da man, auch wenn man sich bei der Entwicklung mit Java-GUI-Bibliotheken wie SWT an eine Plattform bindet, nach wie vor portabel bleibt und das nicht nur theoretisch. Ich denke also nicht, dass sich das über einen Kamm scheren lässt, denn wie bereits erwähnt "hinkt" Mono eben etwas hinterher, wodurch man also gerade im "Bleeding Edge"-Bereich Probleme mit anderen Betriebssystemen bekommen würde.

Ohne ordentlichen Support ist ein Produkt für Firmenkunden immer ein Risiko. Was wenn ein unerwartetes Problem auftritt? Man ruft den Support an und wird geholfen bzw. durch entsprechende Verträge zwingt man ihn helfen zu müssen smile Wenn ich keinen Support hab der mir Hilfe stellt, muss nen halben Tag lang nach einer Problemlösung suchen, dann ist das nen Riesenverlust für die Arbeitskraft.

Auch das kann ich so nicht stehen lassen, denn Unternehmen, die Open-Source Software vertreiben, setzen typischerweise sehr stark auf Support-Verträge, Schulungen für Mitarbeiter, und weiteres wie man an bekannten Unternehmen wie Red Hat, Novell, JBoss Inc. *, MySQL AB sehen kann. Es sollte also klar sein, dass Open-Source Software nicht zwangsweise bedeutet, dass der Support miserabel ist, sondern, wenn man vom kommerziellen Bereich ausgeht, eher das Gegenteil der Fall ist.

grüße

*: Ich weiß durchaus, dass ich JBoss Inc. eigentlich nicht mehr aufzählen dürfte, nachdem sie ja von Red Hat aufgekauft wurden ..

23.12.2006 - 23:52 Uhr

Hallo,

also ich kann dir da leider nur bezüglich der Java Lösung weiterhelfen, wobei du die ja bereits selbst genannt hast, nämlich Jetty, auch wenn dir das wohl nicht ganz klar ist. Lies dir dazu einfach die Dokumentation dazu [1] durch.


>
:
Jetty is an Open Source HTTP Server and Servlet container written in 100% Java. It is designed to be light weight, high performance, embeddable, extensible and flexible.

grüße

[1]: Embedding Jetty - http://docs.codehaus.org/display/JETTY/Embedding+Jetty

09.11.2006 - 00:27 Uhr

Original von Waschbecken

Original von LastGentleman
Am spektakulersten ist der von Bernhard, mit dem MacOSX Style.
Du hast noch nicht an nem Mac gesessen, oder? 🙂 Das Theme hat mir viel mehr die Frage beantwortet, warum die Linux-Desktops auch heute noch so hässlich sind - weil es scheinbar vielen Leuten so gefällt ... seiner sieht mir nämlich eher nach 0815 Gnome/KDE aus.

Gruß

Der Desktop war auch nicht darauf ausgelegt möglichst MacOS X getreu zu sein, da mir persönlich Aqua gar nicht so sehr gefällt. Die Toolbar hat eher praktischen als ästhetischen Hintergrund (und ist übrigens lediglich ein ganz normales Gnome Panel). Was ich aber persönlich nicht in Ordnung finde sind solch provokante Aussagen, wie die bezüglich des Linux Desktops. Ich will an dieser Stelle auch gar nicht weiter darauf eingehen, sondern eigentlich nur sagen, dass es ja wohl wirklich nicht sein muss in einem Thread, der sich um die Desktop-Gestaltung dreht, einen "Linux-vs-Windows Flame-War" anzuzetteln. Man muss ja verständlicherweise nicht alles vertreten, es schadet aber sicher nicht sich ein wenig offener oder - wenn man so will - "toleranter" zu präsentieren.

grüße

07.11.2006 - 23:05 Uhr

Fairerweise muss ich allerdings sagen, dass der Desktop jetzt lediglich für diesen Thread ein wenig in Szene gesetzt wurde. Bei längerem Arbeiten stören mich die meisten Gimmicks eher.

07.11.2006 - 09:11 Uhr

Ich habe bisher immer in C++ programmiert und dort kann man solche konstruktionen basteln:

class Abgeleitet : protected Basis  

Damit sind alle Member von Basis sozusagen "versteckt".

Da hast du in C++ etwas falsch verstanden, denn dieses Konstrukt hat nichts mit Verstecken vom Membern zu tun, sondern eher, wenn man es denn so nennen will, mit Verstecken von Vererbung. Im Falle der protected-Vererbung kannst du beispielsweise Objekte vom Typ Abgeleitet nur innerhalb der Hierachie auf Basis casten. Bei einer private-Vererbung wäre es noch stärker reglementiert, denn die ist nur innerhalb der abgeleiteten Klasse sichtbar.

Auch wenn es jetzt banal klingen mag, aber dieses typische "IST-EIN" Merkmal sollte man stets im Hinterkopf behalten, denn entweder "ist Abgeleitet ein Basis" oder eben nicht. Dazwischen gibt es nichts, auch nicht in C++. In deinem Fall solltest du also eher in Richtung Komposition gehen.

20.10.2006 - 18:47 Uhr

Hallo,

falls deine Nebenobjekte derzeit nur vom Hauptobjekt referenziert werden, würde ich einfach das Hauptobjekt beim Eintreten dieser Eigenschaft wegwerfen und neu erstellen, wodurch der Kreis sozusagen bereits mit einem Bein im Grab steht.

grüße

15.10.2006 - 11:15 Uhr

Was benötige ich, um in etwa auf Augenhöhe mit ASP.NET Webentwicklungen mit Java entwickeln möchte?

Ich würde dir in dem Fall nahe legen, dich mit JavaServer Faces zu beschäftigen.

  • Entwicklungsumgebung (Eclipse?)

Eclipse in der Kombination mit Webtools ist zwar geeignet für Webentwicklungen, aber nachdem du ja bereits mit Visual Web Developer vertraut bist, würde ich dir eher zum Sun Java Studio Creator 1 raten. Vielleicht kommt dir ja das ein oder andere bekannt vor, wenn du die Product Tour 2 durchgehst.

grüße

10.10.2006 - 21:20 Uhr

Um sich nicht dazu verleiten zu lassen Ressourcen zu schließen, weil es einfach zu viel "boilerplate code" ist, sollte man eventuell auch überlegen, ob sich das nicht abstrahieren, bzw. eher "herausziehen" lässt. Sich ständig wiederholende try-finally, bzw. using Blöcke neigen einfach dazu irgendwann vergessen oder sogar bewusst ignoriert zu werden. Folgendes Beispiel sollte ebenfalls zeigen, wie es aussehen könnte.


using System.Data;
using System.Data.Odbc;
using System.Collections.Generic;

// angelehnt an die Klasse JdbcTemplate aus dem Spring Framework
public class AdoTemplate {

    public IList<T> Query<T>(string sql, IRowMapper<T> rowMapper) {
        IList<T> result = new List<T>();

        using (OdbcConnection connection = new OdbcConnection()) {
            using (OdbcCommand command = new OdbcCommand(sql, connection)) {
                using (OdbcDataReader reader = command.ExecuteReader()) {
                    while (reader.Read()) {
                        result.Add(rowMapper.MapRow(reader));
                    }
                }
            }
        }

        return result;
    }
}

public interface IRowMapper<T> {

    T MapRow(IDataReader reader);
}

// so könnte das dann in der Anwendung aussehen
public class AccountDao {

    private AdoTemplate template = /* .. */;

    public IList<Account> GetAccounts() {
        return template.Query("SELECT * FROM Account", new AccountMapper());
    }

    private class AccountMapper : IRowMapper<Account> {

        public Account MapRow(IDataReader reader) {
            return new Account(
                (double) reader["Balance"], 
                (double) reader["CreditInterest"]);
        }
    }
}

08.10.2006 - 23:57 Uhr

Ich würde mich beim Thema Marktanalyse ebenfalls nicht so sehr auf die Anzahl der Magazine am Kiosk
verlassen, sondern z.B: auf entsprechende Statistiken von freiberuflichen Entwicklern (siehe z.B: GULP).

09.09.2006 - 18:55 Uhr

Falls du wirklich lediglich delegieren musst, bietet sich auch folgende Möglichkeit an.


public class UserControl {

    private Button button;

    // ...

    public event EventHandler SomeEvent {
        add {
            button.Click += value;
        }
        remove {
            button.Click -= value;
        }
    }
}

08.09.2006 - 21:02 Uhr

Ich würde im Prinzip VizOnes Vorschlag verwenden, möchte allerdings auch eine andere Möglichkeit zur Diskussion stellen, die zwar einfacher zu implementieren ist, allerdings auch Fehlermöglichkeiten beinhaltet.


using System;
using System.Collections.Generic;

public class Application {

    public static void Main() {
        CustomList<string> strings = new CustomList<string>();

        strings.Add("CustomList");
        (strings as IList<string>).Add("IList");
        (strings as List<string>).Add("List");
            // Hier ist eben das Problem:
            // Falls eine Methode nachwievor die neue Liste als List<T>
            // verwendet, fällt es erstens nicht weiter durch Fehler-
            // meldungen auf und funktioniert eben auch nicht wie 
            // gewünscht. Darum empfehle ich trotzdem VizOnes Vorschlag.

        // ...
    }
}

public class CustomList<T> : List<T>, IList<T> {

    public new void Add(T item) {
        Console.WriteLine("CustomList.Add({0})", item);
        base.Add(item);
    }
}

Ausgabe:
CustomList.Add(CustomList)
CustomList.Add(IList)

grüße
Bernhard Huemer

08.09.2006 - 13:17 Uhr

Gibt es in C# eigentlich ein besseres Workaround, wenn man eine Objektinstanz mehreren Klassen zur Verfügung stellen will?

Ich würde da jetzt nicht einem "Workaround" suchen sondern eher nach dem entsprechenden Design. Bei mehreren Forms in Folge, nennen wir es einfach mal Wizard, habe ich es persönlich immer mit einer Art Datenregistrierung erledigt. Die Vernetzung der einzelnen Forms wurde mit Attributen und Properties implementiert, es könnte also wie folgt aussehen:


public class UserInfoPage : Form {

    [Output]
    public UserInput UserInput {
        get {
            // ...
        }
    }
}

public class SuccessPage : Form {

    [Input]
    public UserInput UserInput {
        set {
            // ...
        }
    }
}

Zusätzlich dazu wurde ein Wizard-Manager erstellt, der die Verwaltung bezüglich der Navigation (z.B: Reihenfolge der Seiten beachten), Ouput- und Input-Paremeter ausliest und setzt, entsprechend validiert, etc..

grüße
Bernhard Huemer

03.09.2006 - 09:55 Uhr

Wenn per default jede Methode virtuell wäre, kannst du dafür wetten, dass es 96% aller Ex-VB'ler dabei belassen werden (Wos'n "final"? Baby ) und dass es selbst die Jungs und Mädels im BCL-Team öfter dabei belassen werden.

Genau deswegen meinte ich ja, dass virtuelle Methoden Standard sein sollten. Auch bei meinen eigenen Klassen wären virtuelle Methoden wesentlich zahlreicher, wenn es Standard wäre und wenn eine virtuelle Methode nicht angebracht ist (eben z.B: weil es sich nicht mit der internen Kapselung vereinbaren lässt), dann versiegel ich sie eben. Derartige Fälle sind meines Erachtens nach aber die Außnahme und dementsprechend sollten sie auch behandelt werden.

Gerade in der BCL wäre der Effekt ziemlich krass, da plötzlich kein inlining innerhalb der BCL Libs mehr möglich wäre und bei jedem x-ten Call in eine solche Methode erst die Implementierung in der VMT nachgeschlagen werden muss.

Fehlendes Inlining bedeutet ja nur, dass der Methodenaufruf nicht eingespart werden kann. Über die Anzahl der Methodenaufrufe würde ich mir nichtmal als C-Programmierer sorgen machen, weil das unbedeutend ist im Vergleich zu anderen Aufgaben im Programm (wie ich eigentlich bereits erwähnt habe).

grüße
Bernhard Huemer

02.09.2006 - 14:02 Uhr

Japp, es wird "nur" in einer VMT nachgeschlagen. Aber das ist a) ein weiterer Sprung[1] und b) verhindert es effektiv inlining.

Selbst mit zusätzlichen Indirektionen wird ein virtueller Methodenaufruf keine negative Auswirkung auf die Gesamtperformance bewirken, siehe z.B: auch die 80/20-Regel, die oft in Verbindung mit Knuths Zitat erwähnt wird. Datenbankoperationen, Zeichnen von Oberflächen oder selbst banale Dinge wie die Konsolenausgabe werden die Performance bestimmen. Als nächstes könnten wir ja auch noch diskutieren ob man i++ oder ++i verwenden soll aufgrund der Performance, wenn du verstehst was ich meine. 😃

grüße
Bernhard Huemer

02.09.2006 - 13:16 Uhr

Ein Framework indem alle Methoden virtuell sind, wäre wahrscheinlich einfach viel zu laaangsaaam. Und damit IMHO absolut sinnbefreit. Augenzwinkern
Eine nicht virtuelle Methode kann direkt als Zeiger auf die Methode in die IL kompostiert werden.
Eine virtuelle Methode bedeutet, dass bei jedem Call die eigentliche Implementierung gefunden werden muss. (Außer die Kombination von Instanz und Methode ist klar definiert wie bei delegates oder Interfaces)

Eine virtuelle Methode bedeutet lediglich eine zusätzliche Indirektion. Ohne jetzt genaue CLR Kenntnisse über die Umsetzung zu besitzen behaupte ich einfach einmal, dass es wie in C++ mit einer vtable umgesetzt wird (oder zumindestens ähnlich). Da gibt es kein "Implementierung finden" sondern lediglich einen simplen Lookup in einer Tabelle, die einen Zeiger auf die Funktion zurückliefert. Insofern würde ich vorsichtig mit Behauptungen ala "viel zu laangsaaam" sein.

Ich glaube auch kaum, dass du für jede Methode im Vorhinein anhand von Profiling-Ergebnissen entscheidest ob du sie virtuell oder nicht virtuell machst. Sinnvoll wäre es lediglich jene Methode zu versiegeln, die sich wirklich als Flaschenhals bezüglich der Performance entpuppen und nicht einfach alles im Vorhinein, nur weil es einer sein könnte. Wie sagte Donald Knuth doch so schön: "Premature optimization is the root of all evil."

grüße
Bernhard Huemer

01.09.2006 - 22:55 Uhr

Original von Traumzauberbaum
Tja nur hast du ne Entscheidung etwas nicht virtuell zu machen deutlich schneller korregiert als andersrum.
Auf der einen Seite musst du ein Schlüsselwort hinzufügen und alles was bisher funktioniert hat, wird auch weiterhin funktionieren.
Auf der anderen Seite musst du eventuell ganze Methoden löschen und Workarounds basteln, oder sogar das Ableiten komplett in Frage stellen.
Also ich weiß genau wofür ich mich im Zweifel entscheide. Und das sollte dann eben logischerweise auch die Standardeinstellung sein.

Die Argumentation ist zwar völlig berechtigt, allerdings kam es mir bereits oft genug vor, dass ich eine Methode überschreiben wollte, wo es einfach Sinn machen würde, ich aber nicht konnte aufgrund eines fehlenden virtual Modifizierers. Über derartige Situationen ärgert man sich zwar, aber ich bemerke bei mir selbst auch, dass ich mit virtual sparsam umgehe, eventuell auch aus Faulheit.

Ein zusätzliches virtual lässt sich zwar einfach hinzufügen, bringt dir aber nichts, wenn du nicht die entsprechenden Quelldateien zur Verfügung hast. Bestes Beispiel: das .NET Framework. Es ist einfach bei weitem nicht alles virtual was virtual hätte sein können und derartige Situationen bergen ähnlichen Workaround-Aufwand kommen aber meiner - in diesem Fall subjektiven - Meinung nach wesentlich häufiger vor.

Ich - als "gebürtiger" Java Programmierer - finde einfach, dass etwas derartig endgültiges wie eine nicht überschreibbare Methode auch wirklich bewusst als solche deklariert werden soll.

Original von Traumzauberbaum
Und sealed ist nun wirklich keine Lösung. Das klingt irgendwie so wie "Warum das Ableiten nicht abschaffen, dann hat man das Problem nicht"

Ich nehme an du beziehst dich auf versiegelte Klassen, ich allerdings auf versiegelte Methoden, die ja in keinster Weise entgegen dem Ableiten wirken.

Original von norman_timo
Ich wollte nur klar machen, dass man "new" kennen muss, um override zu verstehen. Weil wenn es nur override gäbe, dann könnte man auch garkein Schlüsselwort verwenden, und die Methode wäre bei Namensgleichheit einfach überschrieben. Nur weil es noch ein andersbedeutendes "new" gibt, macht in meinen Augen ein "override" (als Schlüsselwort) überhaupt Sinn.

Diesen Ansatz vom Überschreiben bei gleicher Signatur kennt man ja von Java, doch auch dort gibt es zusätzliche Meta-Information (vgl. Attribute) um einen klaren Unterschied zwischen einer neu definierten und einer überschriebenen Methode zu geben. Mit neu definiert ist hier allerdings nicht überdecken gemeint (nachdem "neu" ja auf "new" schließen lässt), sondern wirklich einfach die Definition einer beliebige Methode, die absolut nichts mit der Basisklasse zu tun hat. Insofern macht ein override selbst ohne new Sinn.

01.09.2006 - 18:43 Uhr

Also ich kenne auch Fälle, in denen es sinnvoll ist, das Überschreiben einer Methode nicht zuzulassen (bei denen wäre es dann egal, ob die virtual sind oder nicht), aber kenne keinen sinnvollen Fall für die Verwendung von new.

Der Modifizierer new hat hauptsächlich den Sinn, dass die "Evolution" der Basisklassen ermöglicht wird. Man darf also ohne weiteres Methoden in einer Basisklasse hinzufügen ohne befürchten zu müssen, dass man damit Kompatibilitätsprobleme bekommt, weil z.B: eine abgeleitete Klasse bereits die selbe Methode deklariert hat. Durch derartige Änderungen entsteht zwar klarerweise eine Warnung, die sich allerdings durch den Modifzierer new unterdrücken lässt.

Aber zumindest sprachlich gesehen, ist es unbedingt vonnöten, dass es nicht überschreibbar ist. Vielleicht kann man in einzelnen Fällen ansonsten nicht mehr die Stabilität in der Basisklasse gewährleisten. (Ich könnte mir gut vorstellen, dass z.B. in Bezug auf den File-Handle des Betriebssystems gewisse Methoden, wie z.B. Close, nicht überschrieben werden dürfen sollten, damit auch sichergestellt wird, dass das Handle auf BS-Seite aufgegeben wird etc...)

Da gäbe es ja noch den Modifzierer sealed insofern kann man das auch nicht als Argument für den derzeitigen Standardstatus von Methoden gelten lassen.

Statt Remove und RemoveAt virtual zu definieren, gibt es eine Funktion RemoveItem die virtuell ist.

Dieser Ansatz mag zwar für den erwähnten konkreten Fall ganz angemessen sein, aber wenn man den Gedanken (möglichst naiv) weiterführt, könnte man doch auch behaupten, dass man die Methode Clear mit einer Folge von Aufrufen von RemoveItem implementiert. Zwar ist dieses Beispiel sehr weit hergeholt, da man das kaum derartig umsetzen wird, aber es sollte halt lediglich zeigen, dass man sehr vorsichtig mit solchen entgültigen Entscheidungen sein sollte.

grüße
Bernhard Huemer

01.09.2006 - 11:49 Uhr

Allerdings erhalte ich die Exception "Cannot convert anonymous method block to type 'System.Delegate' because it is not a delegate type".

Jetzt weiß ich nur nicht, ob das evtl. mit dem mir immer noch unbekannten

Register(d);
zusammen hängt.

Das Beispiel war auch eher als eine Art Pseudo-Code gedacht, dass dich lediglich in die richtige Richtung stoßen soll. Hier also eine komplette getestete Version:


public class EventManager {

    private Dictionary<EventHandler, bool> events = 
                new Dictionary<EventHandler, bool>();

    public event EventHandler AllEventsRaised;

    private void Register(EventHandler eventHandler) {
        // Event registrieren und als unausgelöst markieren
        events.Add(eventHandler, false);
    }

    private void MarkRaised(EventHandler eventHandler) {
        // Event als ausgelöst markieren
        events[eventHandler] = true;

        // überprüfen ob bereits alle Events ausgelöst wurden
        if (ShallRaiseEvent()) {
            // Status der Events zurücksetzen, also auf unaungelöst
            ResetEvents();

            if (AllEventsRaised != null) {
                AllEventsRaised(this, new EventArgs());
            }
        }
    }

    private bool ShallRaiseEvent() {
        bool result = true;

        foreach (bool eventRaised in events.Values) {
            // Wenn ein eventRaised false ergibt, wird das Ergebniss auch 
            // false. Es müssen also wirklich alle Events ausgelöst werden.
            result = result && eventRaised;
        }

        return result;
    }

    private void ResetEvents() {
        List<EventHandler> eventHandlers = new List<EventHandler>(events.Keys);
        foreach (EventHandler eventHandler in eventHandlers) {
            events[eventHandler] = false;
        }
    }

    public EventHandler Decorate(EventHandler eventHandler) {
        Register(eventHandler);

        // Einen neuen EventHandler erstellen, der den Aufruf weiterleitet und
        // die interne Verwaltung im Manager übernimmt (das "ausgelöst" Markieren
        // des EventHandlers).
        EventHandler decorated = delegate(object sender, EventArgs e) {
            eventHandler(sender, e);
            MarkRaised(eventHandler);
        };

        return decorated;
    }
}

Zwar wurde es durch die anonyme Methode komplizierter, bringt aber Vorteile in der Anwendung dieses Managers selbst:


static class Application {

    public static void Main() {
        EventManager manager = new EventManager();
        manager.AllEventsRaised += PrintAllEventsRaised;

        SampleEvent sample1 = new SampleEvent();
        SampleEvent sample2 = new SampleEvent();

        sample1.Event += manager.Decorate(SampleHandler1);
        sample2.Event += manager.Decorate(SampleHandler2);

        Console.WriteLine("1. Event wird ausgelöst.");
        sample1.RaiseEvent();

        Console.WriteLine("2. Event wird ausgelöst.");
        sample2.RaiseEvent();

        Console.ReadKey();
    }

    public static void SampleHandler1(object obj, EventArgs e) {
        Console.WriteLine("1. Beispielhandler");
    }

    public static void SampleHandler2(object obj, EventArgs e) {
        Console.WriteLine("2. Beispielhandler");
    }

    public static void PrintAllEventsRaised(object obj, EventArgs e) {
        Console.WriteLine("Es wurden alle Events ausgelöst.");
    }

    private class SampleEvent {

        public event EventHandler Event;

        public void RaiseEvent() {
            if (Event != null) {
                Event(this, new EventArgs());
            }
        }
    }
}

Man sieht also, dass die EventHandler-Methoden selbst überhaupt nichts mehr vom EventManager wissen müssen. Die Anwendung beschränkt sich im Wesentlichen also auf den Aufruf von Decorate, eben so wie es sein soll. Nachdem ich dir damit so quasi die Lösung vorgebetet habe, ist der Lerneffekt nicht mehr derselbe. Ich würde dir also empfehlen, dass du dich selbstständig mit dem Decorator Design-Pattern beschäftigst, da es sich eben doch sehr oft als nützlich erweist.

Mit dem Register hängt es nicht zusammen. Ich denke, dass Register einfach in etwa dasselbe wie AddDelegate macht.

Ich wollte dem Kind halt sozusagen lediglich einen Namen geben um in meinem "quasi-Pseudo-Code" nicht zu sehr aufs Detail zu gehen.

01.09.2006 - 08:50 Uhr

Allerdings war die Anwendung von "delegate.Clone()" nicht notwendig, bzw. es funktioniert sowohl mit als auch ohne.
Oder habe ich dich falsch verstanden?

Das Problem bei deiner Lösung ist ja, dass nach wie vor dein eigener EventHandler die Methode SetEventAsRaised aufrufen muss. Ich stellte mir das ungefähr so vor, allerdings vollkommen ungetestet.


public Delegate Decorate(Delegate d) {
    Register(d);

    Delegate decorated = delegate {
        SetEventAsRaised(d);
        d();
    };

    return decorated;
}

Wobei das Problem hier nur verlagert wird, weil man jetzt statt Aufrufe von SetEventAsRaised jetzt das Dekorieren nicht vergessen darf.

Das würde ich so nicht sehen, denn registrieren musste man das Delegate in jedem Fall. Einen vergessenen Aufruf von Decorate sehe ich jetzt nicht als Problem an, da der Manager dann einfach nicht benutzt wird. Es gibt aber keine "halbe" Registrierung in der das Delegate zwar registriert, aber nie mit SetEventAsRaised entsprechend behandelt wird. Ein vergessener Aufruf von SetEventAsRaised führt dazu, dass das zusammenfassende Event (AllEventsRaised) nie ausgelöst wird, während ein "vergessener" Aufruf von Decorate halt lediglich dazu führt, dass ein Delegate weniger beachtet wird.

31.08.2006 - 12:39 Uhr

Ein Problem mit dem jetztigen EventManager habe ich insofern als dass er "boilerplate code" verursacht. Diese ständigen Aufrufe von SetEventAsRaised sind doch auf die Dauer lästig bzw. leicht zu vergessen. Als kleinen Tipp geb ich mal ein Beispiel, dass zeigt, wie der EventManager zu benutzen sein sollte.


public static void Main() {
    EventManager manager = new EventManager();
    manager.AllEventsRaised += new EventHandler(/* .. */);

    // ...

    click.OnClick += manager.Decorate(new EventHandler(PrintClick));

    // ...
}

public static void PrintClick(object obj, EventArgs e) {
    Console.WriteLine("ClickHandler fired");
}

Zur Methode Decorate gibts halt nur zu sagen, dass sie jedesmal einen neuen EventHandler erstellt 1, bzw. diesen dann auch selbstständig registriert.

grüße
Bernhard Huemer

05.08.2006 - 13:41 Uhr

Hallo,

nicht direkt, sondern halt nur mit WMI und ein wenig Eigeninitative:


public class Share {

    private static List<Share> shares;

    private string uncPath;

    private string localPath;

    private Share(string uncPath, string localPath) {
        this.uncPath = uncPath;
        this.localPath = localPath;
    }

    public static IList<Share> GetShares() {
        if (shares == null) {
            shares = new List<Share>();

            ManagementObjectSearcher shareRetriever =
                new ManagementObjectSearcher("SELECT * FROM Win32_Share");

            foreach (ManagementObject obj in shareRetriever.Get()) {
                string name = (string) obj["Name"];
                string path = (string) obj["Path"];

                shares.Add(new Share(
                        String.Format(@"\\{0}\{1}", Environment.UserDomainName, name), path));
            }
        }

        return shares.AsReadOnly();
    }

    public string UncPath {
        get {
            return uncPath;
        }
    }

    public string LocalPath {
        get {
            return localPath;
        }
    }
}

Aus dieser Sammlung von Shares suchst du dir dann einfach jene, die sich auf dein Laufwerk bezieht und kannst dir damit selbst den passenden UNC Path erstellen.

grüße
Bernhard Huemer

04.08.2006 - 18:21 Uhr

Hallo,

mit dem Stichwort TypeConverter dürftest diesbezüglich genug Informationen bekommen. Für die Eigenschaft Opacity wird ein OpacityConverter 1 benutzt.

grüße

Gewusst wie: Implementieren eines Typkonverters

03.08.2006 - 20:21 Uhr

Hallo,

natürlich wäre das möglich und auch gar keine so schlecht Idee, allerdings wirst du dann innerhalb dieses MSBuild Teils deines Projekts (also jener Teil, der für die C# Komponente zuständig ist) keine Ant Tasks verfügbar haben. Das erwähne ich halt nur aufgrund meiner Aussage "Kann ja sein, dass ihr intern bereits zusätzliche Ant Tasks entwickelt habt, die du auch mit .NET weiterhin verwenden willst."

grüße

02.08.2006 - 23:14 Uhr

Hallo,


public class LocatableStreamReader : StreamReader {

    private int unbufferedPosition;

    public LocatableStreamReader(string path)
            : base(path) {
        // ...
    }

    public int UnbufferedPosition {
        get {
            return unbufferedPosition;
        }
    }

    public override string ReadLine() {
        string line = base.ReadLine();

        // Position aktualisieren
        if (line != null) {
            unbufferedPosition = unbufferedPosition + line.Length;
        }

        return line;
    }
}

Diese Lösung ist es zwar insofern ein "Hack", dass 1. die Position eigentlich nicht wirklich stimmt und 2. dass man dabei wirklich auch immer nur mit ReadLine auslesen darf, nachdem bei anderen Methoden wie Read() die Position nicht aktualisiert wird. Dieses Problem wäre wahrscheinlich auch noch zu bewältigen, wenn man die interne Funktionsweise vom StreamReader kennt (eine Aufgabe für den Reflector), allerdings wollte ich mir das jetzt wegen dieser Antwort nicht mehr antun 🙂

grüße

02.08.2006 - 18:13 Uhr

Hallo,

kommt jetzt ganz drauf an, wieviel Aufwand beim Wechseln des Build Tools entsteht (klarerweise nur bei dir). Kann ja sein, dass ihr intern bereits zusätzliche Ant Tasks entwickelt habt, die du auch mit .NET weiterhin verwenden willst. In diesem Fall würde ich dir empfehlen einen eigenen Ant Task zu schreiben, der eben Unterstützung für keyfiles anbietet (siehe herbivores makefile).

Sollte es aber irgendwie möglich sein, würde ich auf NAnt (die Portierung von Ant für .NET ausgelegt) 1 oder MSBuild (Build Tool von Microsoft) 2 setzen, nachdem einfach vielmehr Tasks bezüglich .NET angeboten werden, z.B: fehlen dir bei Ant ja auch sämtliche Tasks für Unit Tests in .NET.

grüße

02.08.2006 - 17:22 Uhr

Hallo,

um einen etwas universelleren Vorschlag anzubringen:


public class FormCache<CachedForm> where CachedForm : Form, new() {

    /// <summary>
    /// Instanz des Formulars, das zwischengespeichert wird.
    /// </summary>
    private CachedForm cachedForm;

    /// <summary>
    /// Gibt an, ob die letzte Form bereits geschlossen wurde.
    /// </summary>
    private bool closed = true;
        // Nachdem beim ersten Zugriff keine "letzte" Form 
        // existiert, soll eine erstellt werden.

    /// <summary>
    /// Liefert die Instanz des Formulars, das zwischengespeichert wird.
    /// </summary>
    /// <remarks>
    /// Sobald das zwischengespeicherte Formular geschlossen wird (also das 
    /// Closed Event ausgelöst wurde), wird ein neues Formular erstellt. 
    /// </remarks>
    public CachedForm Form {
        get {
            if (closed) {
                cachedForm = new CachedForm();
            }

            return cachedForm;
        }
    }

    private CachedForm CreateForm() {
        CachedForm form = new CachedForm();

        // Event registrieren um das Flag setzen zu können
        form.Closed += delegate {
                closed = true;
            };

        // Close Status zurücksetzen
        closed = false;

        return form;
    }
}

// ...

private FormCache<KonstanteTexteView> instance;

// ...

instance.Form.Show();


grüße

02.08.2006 - 13:07 Uhr

Kann zum Thema CAB die aktuelle Ausgabe der dotnetpro [1] empfehlen, denn auf der Heft CD liegt eine Ausgabe von dotnetpro.tv bei, bei der fast 2 Stunden (1:43:52) lang an einem Beispiel CAB näher gebracht wird. Ansonsten gibt es auch so noch ein paar Artikel wie in der Ausgabe 04/2006 der dotnetpro [2], bzw. ein Online Artikel des .NET Developer Journals [3].

grüße

[1]: Aktuelle Ausgabe 08/2006: http://www.dotnetpro.de/CurrentIssue.aspx
[2]: Ausgabe 04/2006: http://www.dotnetpro.de/issuearchive/issue200604.aspx
[3]: Dependency Injection and Microsoft Windows Forms: http://dotnet.sys-con.com/read/163735.htm

30.07.2006 - 22:17 Uhr

Hallo,

um bei Unit Tests einen einfachen Zugriff auf non-public Member zu bekommen, hab ich mir einen Satz von Klassen geschrieben. Einerseits weil es eben für manche Unit Test Frameworks gar nicht wirklich verfügbar ist (auf die Dauer Reflection zu verwenden ist einfach zu aufwendig), andererseits, weil mir die Lösung durch Code-Genierung (siehe Visual Studio Team System) überhaupt nicht gefällt. Ich hab es da lieber ein wenig dynamischer, aber seht am besten selbst an einem einfachen Beispiel:


using Accessor = mycsharp.Util.Accessor;

public class Person {

    private string name;

    public Person(string name) {
        SetName(name);
    }

    public string Name {
        get {
            return name;
        }
    }

    // nur um auch eine private Methode dabei zu haben
    private void SetName(string name) {
        this.name = name;
    }
}

public interface IPerson {

    string name {
        get;
        set;
    }

    void SetName(string name);
}

public class Application {

    public static void Main() {
        Person person = new Person("Foo Bar");    
        IPerson privatePerson = Accessor.CreateAccessor<IPerson>(person);
        
        Console.WriteLine(person.Name);

        privatePerson.name = "Bar Foo";
        Console.WriteLine(person.Name);

        privatePerson.SetName("Blubber");
        Console.WriteLine(person.Name);

        Console.ReadKey();
    }
}

Das Interface hat eigentlich überhaupt nichts mit der Klasse selbst zu tun, sondern legt mir lediglich eine Schnittstelle für den Zugriff auf non-public Member fest. Die Namen der Eigenschaften, bzw. Methoden werden dabei einfach übernommen, wie man auch im Beispiel sieht. Um dies zu ermöglichen habe ich auch eine eigene Implentierung von einem Proxy (siehe java.lang.reflect.Proxy), auch wenn es vllt bereits die 100. ist. Leider ist es derzeit noch nicht vollständig, so fehlt z.B: die Unterstützung für private Events, out und ref Parameter, alles in allem Dinge, die ich beim Testen eigentlich ohnehin nicht benötige, daher wurde es auch nicht implementiert.

Dachte mir, dass das eventuell ganz gut in die Komponentensammlung passt 🙂

grüße

26.07.2006 - 21:12 Uhr

Gut, zugegeben dein Beispiel stellt eine Schwachstelle in meinem Vorschlag dar.

Original von Traumzauberbaum
Da Worte wohl nicht helfen...

Wie sollen Worte denn auch helfen, wenn du von was völlig anderem redest? In deinem Beispiel zeigen sich Synchronisationsprobleme bei den vom Manager verwalteten Objekten, während du andauernd von Deadlocks gesprochen hast. Auch in deinem Beispiel, sofern man einen kleinen Fehler ausbessert, treten unter keinen Umständen Deadlocks auf.

Womit wir allerdings eigentlich wieder am Anfang stehen würden, denn die Idee mit dem Rollback durch Exceptions ist aufgrund des enormen Aufwands einfach nicht durchführbar. Man müsste ja in jede Methode einen try-finally-Block packen und jede einzelne Zeile rückgängig machen, womit man sozusagen jede Methode doppelt schreibt (ungeachtet von Problemen wie man ein Console.WriteLine, etc.. rückgängig macht). Der Vorschlag mit dem gemeinsam genutzten Lock hat halt nach wie vor den Nachteil, dass die gesamte Parallelisierung umsonst ist, was in einem anscheinend event-betriebenen System absolut nicht tragbar ist ..

ernüchterte grüße

26.07.2006 - 16:14 Uhr

Original von Traumzauberbaum
Folge:
A) Es laufen zwei Funktionen beim Manager nebeneinander, weil das nicht durch ein lock verhindert wurde

Diese Folge kann man getrost vergessen, da sehr wohl gelocked wird.

Original von Traumzauberbaum
B) Das ausgelöste Event kann seine Funktion nicht fortsetzen, weil das nebeneinander ausführen von Funktionen vom Manager durch ein lock verhindert wird. Der Manager kann aber auch nicht fortsetzen, weil das Objekt noch das Event abarbeitet => Deadlock

Das ausgelöste Event versucht also über den ManagerDecorator auf den Manager zuzugreifen und scheitert am lock, genauer gesagt am TryLock. Als nächstes wird also Monitor.Wait() aufgerufen, wodurch der objectLock (also der des ObjectDecorators) wieder freigegeben wird und der Manager kann nun fortsetzen. Sobald er seine Arbeit erledigt hat, ruft er Monitor.Pulse() auf um den Thread des Events wieder fortsetzen zu lassen.

Probleme gabs eventuell noch beim Rücksprung, da just in diesem Moment der Lock des Monitors eventuell noch nicht wieder frei ist, da der Rücksprung entweder noch nicht vollzogen ist oder der Manager weitere Methoden vom Objekt aufrufen muss. *Lock muss erst freigegeben werden, da die Ausführung noch immer vor dem Ende des lock()-Blockes steht:
Es wird solange 100ms gewartet, bis das Ende des Blockes im Manager erreicht wurde.

*Lock muss erst freigegeben werden, allerdings muss der Manager noch weitere Operationen am Objekt ausführen:
Der gesamte Ablauf wiederholt sich einfach nur.

Man sollte mittlerweile allerdings das Prinzip erkennen und verstehen, dass der ManagerDecorator versichert, dass das Objekt (und somit auch der Manager) entweder beide Locks oder gar keinen sichern kann, was eben durch den Wait&Pulse Mechanismus ermöglicht wird.

grüße

26.07.2006 - 15:41 Uhr

Original von Traumzauberbaum
Der Manager soll doch aber auch synchronisiert sein.
Wenn du dir da den Decorator nicht dafür gedacht hast, muss man es anders machen. [...]

Wie gesagt, darum muss sich der Manager selbst auf "klassische" Weise kümmern.


public class Manager : IManager {

    private object padlock = new object();

    public object DoOperation() {
        lock (padlock) {
            // ...
        }
    }

    // ...
}

Original von Traumzauberbaum
[...]

  • Event löst in Decorator A für Objekt A Methode A aus: Objekt A greift auf Methode A vom Decorator B für Manager A zu
    [...]

Dieser Fall kann eigentlich gar nicht eintreten, da ObjectDecorator, IObject und ManagerDecorator eine 1:1 Beziehung haben, was bedeutet, dass Object A ManagerDecorator B gar nicht kennen kann.

Wenn man die Ablaufsequenz also nochmal neu gestaltet:

  • Event löst in Decorator A für Objekt A Methode A aus: Objekt A greift auf Methode A vom Decorator A für Manager A zu
  • Manager A ruft für Decorator A für Objekt A Methode B auf

Es gibt in dem Sinn aber eigentlich nur 3 Möglichkeiten, wie das ablaufen kann:*Manager A und ObjectDecorator A sind zusammen schnell genug um beide Locks (vom Manager und von ObjectDecorator) zu sichern, wodurch Manager A die Operation als Erster ausführt. *Manager A und ObjectDecorator A (vom Event aus gesehen) besitzen jeweils ein Lock, wodurch der ManagerDecorator den Thread vom Event warten lässt (Monitor.Wait()), was bedeutet, dass der Manager A sich beide Locks sichern kann und führt die Operation als Erster aus. *ObjectDecorator A, bzw. ManagerDecorator A sichern sich zusammen beide Locks und Object A führt die Operation als Erster aus.

26.07.2006 - 14:17 Uhr

Original von Traumzauberbaum
Überleg dir das analog für den Manager.

Auch bei dem müsste man also immer über den Decorator zugreifen.

=> Deadlocks

Das kann man gar nicht analog umsetzen, denn der ManagerDecorator hat nicht die Aufgabe ein Manager Objekt zu synchronisieren. Das muss der Manager schon selbst tun. Ein ManagerDecorator überprüft auf Deadlocks und priorisiert einfach sozusagen den anderen Thread, den anderen Weg, der vom Manager aus losging durch einen Aufruf von Monitor.Wait() wodurch der eigene Thread erstmal still gelegt wird.

Wodurch auch klar wird, dass nicht immer über einen Decorator zugegriffen werden muss, sondern, wie im 1. Vorschlag auch, nur beim Aufruf von einem Objekt, das vom Manager verwaltet wird. Sämtliche "Außenstehende" Objekte, die nichts mit diesem System zu tun haben, brauchen sich diesbezüglich keine Gedanken machen, da, wie bereits gesagt, der Manager sich sehr wohl selbst synchronisieren muss.

Aber um trotzdem einen halbwegs ähnlichen Ablauf darzustellen:
Event löst in Decorator A für Objekt A Methode A aus: Objekt A greift auf Methode A vom Decorator A für Manager A zu.
Event löst in Decorator A für Objekt A Methode B aus: Objekt B greift auf Methode B vom Decorator A für Manager A zu.

Nachdem beim 2. Aufruf allerdings der lock nicht mehr verfügbar ist, wird erstmal gewartet und zwar genau solange bis das der 1. Aufruf wieder beim ObjectDecorator angelangt und schließlich Monitor.Pulse() aufruft.

Was allerdings Probleme machen wird, ist folgende Ablaufsequenz:
Event löst in Decorator A für Objekt A Methode A aus: Objekt A greift auf Methode A vom Decorator A für Manager A zu.
Event löst in Decorator B für Objekt B Methode A aus: Objekt B greift auf Methode A vom Decorator B für Manager A zu.

Zwar wird wie gehabt der 2. Aufruf blockiert, da der lock nicht verfügbar ist, allerdings gibt es für Objekt B kein dazugehöriges Monitor.Pulse(), da dies eigentlich ein anderes Problem ist. Es ist kein Deadlock im Sinne von der Aufgabenstellung, sondern es würde sich lösen, wenn man einfach ein wenig wartet:


while (!Monitor.TryEnter(managerLock)) {
    // Nachdem wir uns hier eh irgendwie in einem lock(objectLock) Block
    // befinden, da dieser Aufruf irgendwie vom ObjectDecorator ausging,
    // dürfen wir auch ein Wait auf objectLock ausführen.
    // Eventuell ist es gar kein Deadlock und er löst sich in einer gewissen
    // Zeit (100ms) von selbst auf.
    Monitor.Wait(objectLock, 100);
}

grüße

26.07.2006 - 13:10 Uhr

Original von Traumzauberbaum
Nein ich meinte wenn die Funktionen nicht auf den Manager zugreifen.

Folgendes Beispiel soll ja auch synchronisiert werden:

Event löst in Objekt A Methode A aus
Manager greift auf Objekt A Methode B zu

Genau da kannst du es nicht vermeiden, dass sich die Objekte auch selbst um synchronisation kümmern, das ist Teil der Anforderung.

Original von Bernhard
Wichtig ist allerdings, dass nun wirklich jeder (also auch der Manager selbst) über den Decorator auf diese Objekte zugreifen, da das Objekt sonst nicht threadsicher ist.

Wenn diese Bedingung eingehalten wird, gibt es das von dir geschilderte Problem gar nicht, denn dann sieht der Zugriff folgendermaßen aus:

Event löst in Decorator A für Objekt A Methode A aus
Manager greif auf Decorator A für Objekt A Methode B zu

Um es nocheinmal klar zu stellen:
Um einen Aufruf eines vom Manager verwalteten Objekts zu synchronisieren, wird ein Decorator davor gesetzt.

grüße

26.07.2006 - 12:29 Uhr

Original von Traumzauberbaum
Das Problem ist doch, dass dein Code unwirksam ist, er behebt keine Deadlocks.

Falls man selbst zusätzlich synchronisiert natürlich nicht, habe ich aber auch selbst erwähnt.

Original von Bernhard
Der Nachteil an dieser Lösung ist halt, dass wirklich sichergestellt sein muss, dass sich die vom Manager verwalteten Objekte nicht um Synchronisierung kümmern. Sollten sie das dennoch tun, wird es wieder zu Deadlocks führen.

Original von Traumzauberbaum
[...] Oder du lässt dabei völlig außer acht, dass die Objekte auch synchronisiert sein sollen, wenn sie nicht mit dem Manager arbeiten. [...]

Ob die vom Manager verwalteten Objekte auf Informationen vom Manager zugreifen oder dessen Status irgendwie verändern, also mit ihm "arbeiten", ist bei der Synchronisierung von diesen Objekten in erster Linie völlig unerheblich.

ObjectDecorator synchronisiert den Zugriff auf Objekte, ob da jetzt ein zusätzlicher ManagerDecorator verwendet wird, ist völlig irrelevant, da ansonsten eben Monitor.Pulse() nichts macht.

Falls du mit "nicht mit dem Manager arbeiten" meinst, dass sie absolut gar nichts mit diesem zu tun haben, also z.B: auch nicht verwaltet werden, dann stellt sich halt die Frage: Warum sollten sie dann auch diesen "Quasi-Vertrag" einhalten und sich nicht synchroniseren, wenn es ohnehin nicht benötigt wird?

Jetzt kann es halt noch sein, dass es eventuell nur für ein paar Objekte gilt, allerdings nicht für ganze Klassen, dass sie nichts mit dem Manager zu tun haben, man also je nach Instanziierung umschalten müsste zwischen synchronisiertem Zugriff auf das Objekt selbst und nicht synchronisiertem. In diesem Fall würde ich eine Synchronize Methode im Manager oder ähnlichem anbieten und den Default-Status nach wie vor bei unsynchronisiert belassen.


public static IObject Synchronize(IObject obj) {
    return new ObjectDecorator(obj, new object());
}

Solche bereits dekorierte und somit synchronisierte Objekte darf man dann allerdings wieder nicht vom Manager verwalten lassen, wegen der Deadlock Gefahr.

26.07.2006 - 11:50 Uhr

Hallo,

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=564940&SiteID=1

Zusätzliche Verbesserungen von der vorgestellten Lösung wär halt z.B: das Implementieren von IList, etc..

P.S: Wieder mal tut es mir leid für das Ausgraben von Threads ..

26.07.2006 - 11:28 Uhr

Original von Traumzauberbaum
Am Ende ist das 2. auch nur eine umständliche Variante bei der auf ein gemeinsames Objekt gelockt wird.

Eigentlich absolut nicht, denn dieses objectLock Objekt soll ja für jedes vom Manager verwaltete Objekt neu erstellt werden. Genau zwei Decoratoren (und auch wirklich sonst nichts) verwenden diesen Lock wegen dem Wait&Pulse-Mechanismus.

26.07.2006 - 10:39 Uhr

Hallo nochmal,

heute morgen ist mir ein besserer Ansatz mit einem Wait&Pulse-Mechanismus eingefallen. Die Idee dahinter ist ganz einfach, dass man das synchronisieren aus diesen vom Manager verwalteten Objekten herauszieht und ebenfalls gesondert behandelt, in diesem Fall eben dekoriert. Das bedeutet das sämtliche Objekte erst über den Zugriff durch den Decorator auch threadsicher werden, allerdings kann man dann auch eben in diesem Decorator das Synchronisieren zentral verwalten und auf den Manager anpassen.

Ich werd das jetzt allerdings nicht mehr so genau erklären, nachdem sich vom letzten Vorschlag von mir nicht so viel geändert hat, so dass gewisse Konzepte noch immer gleich bleiben.


using System;
using System.Threading;

public interface IManager {

    object DoOperation();
}

public interface IObject {

    void DoOperation();
}

public class ManagerDecorator : IManager {

    // Manager, der dekoriert werden soll
    private IManager manager;

    // internes Lock Objekt des Managers
    private object managerLock;

    // Lock Objekt, das für das Objekt benutzt wird
    private object objectLock;

    public ManagerDecorator(IManager manager, object managerLock, object objectLock) {
        // auf null prüfen ..

        this.manager = manager;
        this.managerLock = managerLock;
        this.objectLock = objectLock;
    }

    // auch hier gilt wieder:
    // Bei häufiger Verwendung würd ich eine eigene Klasse
    // um den Monitor herum schreiben, die mit einem using
    // benutzt werden kann (siehe Link im oberen Beitrag).
    public object DoOperation() {
        while (!Monitor.TryEnter(managerLock)) {
            // Nachdem wir uns hier eh irgendwie in einem lock(objectLock) Block
            // befinden, da dieser Aufruf irgendwie vom ObjectDecorator ausging,
            // dürfen wir auch ein Wait auf objectLock ausführen.
            Monitor.Wait(objectLock);
        }
        try {
            return manager.DoOperation();
        } finally {
            Monitor.Exit(managerLock);
        }
    }
}

public class ObjectDecorator : IObject {

    // Objekt das dekoriert werden soll
    private IObject obj;

    // Lock für dieses Objekt
    private object objectLock;

    public ObjectDecorator(IObject obj, object objectLock) {
        // auf null prüfen ..

        this.obj = obj;
    }

    // auch hier gilt wieder:
    // Bei häufiger Verwendung würd ich eine eigene Klasse
    // um den Monitor herum schreiben, die mit einem using
    // benutzt werden kann (siehe Link im oberen Beitrag).
    public void DoOperation() {
        lock(objectLock) {
            obj.DoOperation();

            Monitor.Pulse(objectLock);
        }
    }
}

// ...
// so in etwa könnte die Erstellung eines Objekts im Manager aussehen:

object objectLock = new object();
CustomObject obj = new CustomObject(new ManagerDecorator(this, this.managerLock, objectLock));
return new ObjectDecorator(obj, objectLock);

// ...

Dadurch ist man nicht mehr anfällig auf "catch(Exception ex)"-Blöcke und muss sich beim Entwickeln der Objekte gar nicht mehr um Synchronisierung kümmern. Wichtig ist allerdings, dass nun wirklich jeder (also auch der Manager selbst) über den Decorator auf diese Objekte zugreifen, da das Objekt sonst nicht threadsicher ist.

Der Nachteil an dieser Lösung ist halt, dass wirklich sichergestellt sein muss, dass sich die vom Manager verwalteten Objekte nicht um Synchronisierung kümmern. Sollten sie das dennoch tun, wird es wieder zu Deadlocks führen. Jetzt erkennt man auch, dass sich die beiden Lösungen bei den Vor- und Nachteilen genau ergänzen.

Hoffentlich habe ich jetzt insgesamt nicht doch zuwenig erklärt ..

grüße
Bernhard Huemer

26.07.2006 - 00:25 Uhr

Hallo,

eine Lösung mit einem gemeinsamen Lock Objekt würde ich nicht empfehlen, da dann sämtliche Objekte, die vom Manager verwaltet werden, gleichzeitig synchronisiert werden, auch wenn es gar nicht nötig wäre, wodurch jeglicher Vorteil von Parallelisierung verloren geht.

Mir fällt da im Moment allerdings auch nur eine Lösung mit einem Rollback durch eine Exception ein, wie Traumzauberbaum es bereits erwähnte. In folgender Implementierung davon wird dabei auch diese zyklische Abhängigkeit aufgebrochen um einen "Mittelsmann" zu ermöglichen, der diese Synchronisierung übernimmt.


public interface IManager {

    object DoOperation();
}

public interface IObject {

    void DoOperation();
}

public class LockAcquireException : ApplicationException {

    // ...
}

public class ManagerDecorator : IManager {

    // Manager, der dekoriert werden soll
    private IManager manager;

    // internes Lock Objekt des Managers
    private object padlock;

    public ManagerDecorator(IManager manager, object padlock) {
        // auf null prüfen ..

        this.manager = manager;
        this.padlock = padlock;
    }

    // falls mehrere Methoden zu dekorieren sind:
    // [URL]http://www.interact-sw.co.uk/iangblog/2004/03/23/locking[/URL]
    public object DoOperation() {
        // falls ein Deadlock aufgetreten ist, findet ein "Rollback" statt
        if (!Monitor.TryEnter(padlock)) {
            throw new LockAcquireException();
        }
        try {
            return manager.DoOperation();
        } finally {
            Monitor.Exit(padlock);
        }
    }
}

public class ObjectDecorator : IObject {

    private IObject obj;

    public ObjectDecorator(IObject obj) {
        // auf null prüfen ..

        this.obj = obj;
    }

    public void DoOperation() {
        try {
            obj.DoOperation();
        } catch (LockAcquireException ex) {
            // Dem restlichen Programm Zeit geben, damit nicht 
            // gleich wieder ein Deadlock auftritt.
            Thread.Sleep(100);

            // erneut versuchen durch Rekursion
            DoOperation();
        }
    }
}

// ...
// so in etwa könnte die Erstellung eines Objekts im Manager aussehen:

CustomObject obj = new CustomObject(new ManagerDecorator(this));
return new ObjectDecorator(obj);

// ...

Wichtig ist dabei, dass sämtliche vom Manager verwaltete Objekte nur über den Decorator (ManagerDecorator) mit diesem kommunizieren, wovon sie allerdings dank dem Interface eh nichts mitbekommen sollten. Außerdem muss sichergestellt werden dass von aussen (z.B: sind die Events mit "von aussen" gemeint, also einfach alles ausser der Manager selbst) auch nur über den ObjectDecorator auf die vom Manager verwalteten Objekte zugreifen, damit diese "Rollback"-Exception korrekt behandelt wird.

Der Vorteil dieser Lösung ist halt, dass innerhalb der vom Manager verwalteten Objekte gelocked werden kann, wie es beliebt, ohne dass ein Deadlock auftreten wird, allerdings ist sie anfällig für "catch (Exception ex)"-Blöcke. Außerdem finde ich es auch nicht 100% optimal gelöst, da der Fehler (also der Deadlock) immer nur dann behandelt wird, wenn er bereits aufgetreten ist, anstatt ihn irgendwie zu vermeiden.

Ein weiterer Nachteil ist halt, dass die vom Manager verwalteten Objekte absolut exception-sicher entworfen werden müssen, so dass keine Ressourcen geöffnet bleiben, falls ein Rollback aufgrund eines Deadlocks stattfindet.

grüße
Bernhard Huemer

21.07.2006 - 10:25 Uhr

Weil die Methode GetType eigentlich einen "assembly-qualified name"[1] erwartet, nur falls sich der gesuchte Typ in der mscorlib.dll oder in der aktuellen Assembly befindet reicht auch die Angabe des mit den Namespace qualifzierten Typ.

Eine weitere Möglichkeit wäre Assembly.GetType()[2] zu verwenden, wobei man auch bei dieser Vorgehensweise nicht darum herumkommt zu wissen, in welcher Assembly sich dieser Typ befindet.

Am einfachsten ist es eben, sofern der entsprechende Typ bereits zur Compile Zeit bekannt ist, den typeof Operator zu verwenden.

grüße

[1] http://msdn2.microsoft.com/en-us/library/system.type.assemblyqualifiedname.aspx
[2] http://msdn2.microsoft.com/en-us/library/y0cd10tb.aspx

20.07.2006 - 15:13 Uhr

Hallo,

zwar mag es für diesen Zweck recht sinnlos sein auf eine Java API zurückzugreifen, allerdings muss das deswegen nicht zwangsweise immer eine schlechte Idee sein (man denke dabei nur an den Fall, dass man auf Enterprise JavaBeans zugreifen muss).

http://www.devx.com/interop/Door/18896
http://msdn.microsoft.com/vstudio/java/interop/default.aspx

Für alle, die ein dotnetpro Abonnement bestellt haben:
http://www.dotnetpro.de/articles/onlinearticle1435.aspx

.. oder auch einfach nur bei Google suchen.

20.07.2006 - 12:18 Uhr

Hallo,

Er findet hier tatsächlich alle Klassen, doch der is-Vergleich schlägt immer fehl, warum?

Ich zu dem Zeitpunkt der Vergleiche noch keinerlei Instanzen der Klassen gemacht, liegt es eventuell daran?

Es liegt halt ganz einfach daran, dass ein Objekt vom Typ Type eben kein MarshalByRefObject ist, da Type auch nicht davon erbt. Zwar ist es verständlich was du beabsichtigst, aber warum sollte der Typ Type in der Hinsicht besonders reagieren?

Zur Lösung:
Type.IsSubClassOf Method

grüße

19.07.2006 - 19:41 Uhr

Hallo,

zwar kommt die Antwort etwas spät, da ich allerdings bei der Suche drauf gestossen bin und die Frage für ganz interessant hielt:

Ich nehme an du - wobei die Antwort vorallem an weitere Suchende gerichtet ist - suchst Thread.Sleep(), genauer gesagt Thread.Sleep(0).

[...] Specify zero (0) to indicate that this thread should be suspended to allow other waiting threads to execute. [...] Thread.Sleep Method

grüße

14.07.2006 - 19:18 Uhr

[...] ich hätte auch viel lieber eine Lösung, die durch Vererbung erreicht wird vorstellen wollen, vielleicht habe ich es auch deshalb nicht verstanden 😉

In dem Artikel geht es aber eben gerade darum, dass sich die Klasse PersistentSet eigentlich so ganz und gar nicht in die Set Hierachie bringen lässt. Erst in Verbindung mit dem generischen Paradigma, welches allerdings nicht erwähnt wurde, lässt es sich wie gewünscht lösen. Andernfalls kann man höchstens Iteratoren und sonstige Lese Zugriffe auf ein gemeinsames Interface auslagern.

grüße

13.07.2006 - 20:12 Uhr

Hallo,

ich versuch mal das Real World Beispiel einfacher darzustellen.


// ...

public interface IPersistentObject {

    // ...
}

public interface ISet {

    void Add(object obj);
}

public interface IPersistentSet : ISet {

    void Add(IPersistentObject obj);
}

public class Application {

    public static void Main() {
        IPersistentSet persistentSet = /* ... */;
        persistentSet.Add(new object()); // gibt keinen Compiler Fehler
    }
}

Das Problem dabei ist eben, dass man in die PersistentSet Objekte einfügen kann, die nicht zwangsweise serialisierbar sind (in diesem Fall eben IPersistentObject implementieren), nachdem das so durch die Schnittstelle vom Set vorgegeben wird. Eigentlich war gedacht die Ähnlichkeit der Schnittstellen auszunutzen um einen analogen Zugriff über das Basisinterface (hier: ISet) anzubieten für diverse Zusatzfunktionalitäten, wie z.B: Ausgabe mit Hilfe eines Iterators, etc.. (siehe dein pdf).

Alles was man machen könnte, wäre die entsprechende Methode überschreiben und versuchen das Objekt auf eine PersistenObject zu casten, allerdings kann das eben auch in einer Exception enden. Kein Benutzer der Set würde allerdings eine InvalidCastException o.ä beim Aufruf der Methode Add erwarten, wodurch eine "Schnittstelle gebrochen" wurde.

Die Lösung mit dem Kompilierfehler ist allerdings meines Erachtens nach schwachsinnig, da man dies mit Generics bzw. Templates trotzdem als Vererbung darstellen kann.


public interface ISet<T> {

    void Add(T obj);
}

public interface IPersistentSet : ISet<IPersistentObject> {

    // ...
}

grüße

25.04.2006 - 19:02 Uhr

Hallo,

zum "height:100%;" gibt es einen ganz guten Artikel:
http://www.quirksmode.org/css/100percheight.html

grüße
Bernhard Huemer

25.04.2006 - 18:13 Uhr

Hallo,

ich nehme mal an diese Frage bezieht sich auf deinen Artikel, denn in diesem Fall ist die Lösung ganz leicht und benötigt nichtmal ein Singleton oder ähnliches:


using System;
using System.Collections.Specialized;

public class ConfigurationSettings
{
    private static NameValueCollection appSettings;

    public static NameValueCollection AppSettings {
        get {
            // Auch hier gilt wieder:
            // Hat zwar Ähnlichkeit mit einem Singleton, ist aber keins.
            if (appSettings == null) {
                Load();
            }

            return appSettings;
        }
    }

    public static void Load() {
        // Hier muss eben sichergestellt werden, dass appSettings erst dann initalisiert
        // wird, wenn bereits alles über die Bühne gelaufen ist. Am einfachsten geht das
        // indem man den Load-Prozess ansich mit einer "temporären" NameValueCollection
        // erledigt.
        NameValueCollection result = new NameValueCollection();

        // .. befüllen ..

        appSettings = result;
    }
}

Falls die Frage eher generell gerichtet war, dann bietet sich eben, wie bereits von svenson erwähnt, das Singleton Pattern an.

grüße
Bernhard Huemer