Laden...
P
Pulpapex myCSharp.de - Member
Java-Entwickler Rostock Dabei seit 22.07.2003 939 Beiträge
Benutzerbeschreibung

Forenbeiträge von Pulpapex Ingesamt 939 Beiträge

08.01.2006 - 18:37 Uhr

Hier sollte sich was inden lassen:
www.regxlib.com

Du kannst aber auch die statische Methode System.IO.Path.IsPathRooted verwenden. Die prüft auch nicht im Dateisystem, nur den Pfad-String.

"C:\WINDOWS\system32\msvbvm60.dll\3"

Das ist übrigens ein gültiger Pfad. Die Dll-Datei könnte ja auch ein gleichbenannter Ordner sein.

Gruss
Pulpapex

14.12.2005 - 02:00 Uhr

Ich hoffe das mir keiner mein Array als Stack auslegt.

Ein Stack, kein Zweifel.

Denn in der iMove-Schleife wird das Array am Ende mit

aiFrom [iDisk-1] = iTmp;

verändert. Ohne diese Zeile wäre es ok, denn du würdest ohne zusätzliche Zwischenspeicherstruktur - Stack, Array, was auch immer - auskommen.

Aber das Turm-von-Hanoi Problem ist wirklich ohne "rekursiven" Zwischenspeicher zu lösen. Laut Buneman und Levy (und Wikipedia) muss in jedem Schleifendurchlauf nur bekannt sein, wie gross die Scheiben auf den Türmen sind. Man braucht also immer nur die aktuellen Werte.

Ausführen bis der gesamte Turm verschoben wurde:1.1. Die kleinste verschiebbare Scheibe auf den Stapel rechts von ihr legen
(bzw. auf den ersten, wenn die Scheibe auf dem letzten Stapel liegt).

1.2. Die zweitkleinste verschiebbare Scheibe auf den einzig möglichen Stapel verschieben.

Gruss
Pulpapex

09.12.2005 - 23:59 Uhr

Warum nicht doch die Factory?

// Factory instanziieren.
CommandFactory factory = new CommandFactory();

// Commands registrieren.
factory.RegisterCommand("Move", typeof(MoveCommand), new MoveParameters());
factory.RegisterCommand("Exit", typeof(ExitCommand), new ExitParameters());

// Move-Command erzeugen.
MoveCommand move = (MoveCommand)factory.CreateCommand("Move");

Resultat: jedes MoveCommand-Objekt bekommt die selbe MoveParameters-Instanz gesetzt, die SetParameters-Methode kann problemlos in der Basisklasse Command definiert werden, erneute Definition in den Command-Ableitungen ist nicht erforderlich.

Spricht doch nichts dagegen. Flexibler als eine statische Methode ist es auf jeden Fall (auf statische Methoden, die den Zustand der Klasse ändern, würde ich ohnehin immer verzichten wollen).

Gruss
Pulpapex

07.12.2005 - 23:22 Uhr

Alles mit einer Anfrage erschlagen wird nicht klappen.

Entweder du verwendest für den zweiten Teil eine zusätzliche Anfrage:

SELECT artname1, fnr
FROM BIO_Arten
ORDER BY artname1;

Die Zuordnung der Flächen ergibt sich dann anhand der Sortierung, alle Flächen einer Art stehen im Ergebnis hintereinander.

Oder du fragst die Flächen für jeden Artnamen einzeln ab:

SELECT fnr
FROM BIO_Arten
WHERE artname1 = ?;
06.12.2005 - 01:16 Uhr

Guck dir lieber die Möglichkeiten von SQL näher an. Die Arbeit mit der Txt-Datei hättest du dir eigentlich sparen können.

Folgende SQL-Anweisung liefert beispielsweise alle Artnamen und wie oft sie in der Tabelle vorkommen:

SELECT artName, COUNT(*) AS artCount
FROM fl_art
GROUP BY artName;

In welchen Flächen welche Art vorkommt, bekommt man noch einfacher raus.

Gruss
Pulpapex

02.12.2005 - 22:37 Uhr

Du müsstest mal untersuchen wie es sein kann, dass zwei Sessions gleichzeitig offen sind. Vielleicht vergisst du irgendwo eine Session zu schliessen oder öffnest unbeabsichtigt eine neue.

Wenn es geht, sollte man vermeiden, mehrere Sessions parallel offen zu haben. Ist natürlich nicht immer möglich. Mit mehreren Sessions muss aber unbedingt sichergestellt werden, dass persistente Objekte nicht zwischen den Sessions wandern können.

Ich hatte dieses Problem mal im Zusammenhang mit "geschachtelten" Transaktionen. Innerhalb einer laufenden Transaktion sollte eine zweite durchgeführt werden, um Statusänderungen und Log-Meldungen in die Datenbank zu schreiben, damit diese auch bei einem Rollback der äußeren Transaktion erhalten bleiben.

Folgendes Szenario mit zwei Sessions/Transaktionen löste die Exception aus:

                  Status wieder speichern
Status laden               |
  |                        v
  v                      [---]
[------------------------------]

Die Lösung sah bei mir so aus:

Status laden      Status wieder speichern
   |                       |
   v                       v
 [---]                   [---]
[------------------------------]

Gruss
Pulpapex

// Verschoben nach: Datentechnologien

21.11.2005 - 22:07 Uhr

Falls das Datenmodell noch geändert werden kann, es gibt eine sehr elegante Möglichkeit Bäume in Tabellen abzubilden.

Hatte Noodles mir mal gezeigt, das Datendesign nennt sich "Nested Sets". Der grosse Vorteil: man kann mit einer einzigen SQL-Anfage einen kompletten Teilbaum laden. Oder man kann mit einer Anfrage zählen lassen, wieviele Knoten ein Teilbaum hat. Oder man kann sich den Pfad von der Wurzel zu einem Knoten geben lassen usw.

Intern basiert das Ganze auf einer Pre-Order Anordnung der Baumknoten. SQL-Anfragen laden quasi Teilbereiche aus dieser Pre-Order Anordnung. Aus der geladenen Liste kann der Teilbaum rekonstruiert werden.

Nachteil bei "Nested Sets" ist der Aufwand bei Änderungen. Um ein Element einzufügen oder einen Teilbaum zu löschen, müssen alle in der Pre-Order Reihenfolge nachfolgenen Baumknoten per SQL-Update aktualisiert werden. Das benötigt zwar selbst nur 2 SQL-Anweisungen - 1. nachfolgende Knoten verschieben (Platz schaffen für einen neuen Knoten schaffen oder frei gewordene Lücke schliessen), 2. Knoten einfügen oder löschen - das ist aber dennoch sehr aufwändig.

Kannst du dir ja mal angucken:

Gruss
Pulpapex

17.11.2005 - 00:26 Uhr

Hier ist der Fehler:

public interface IPlugin : IPlugin1, IPlugin2
{
}

public interface IPlugin1
{
  string Name {get;}
}

public interface IPlugin2
{
  string Wert {get;}
}

Da erbt IPlugin von IPlugin1 und IPlugin2.

Du möchtest es aber so haben:

public interface IPlugin
{
}

public interface IPlugin1 : IPlugin
{
  string Name {get;}
}

public interface IPlugin2 : IPlugin
{
  string Wert {get;}
}

Gruss
Pulpapex

16.11.2005 - 12:57 Uhr

Eigentlich sind das Formatangaben. Genauso gut könntest du fragen, wie man kursive Zeichen in einem String speichert.

Aber vielleicht gibt es wirklich was dafür in Unicode. Man kann ja in Unicode Zeichen kombinieren, wodurch das resultierende Zeichen anders dargestellt wird.

Vielleicht findest du hier was: http://www.unicode.org/charts/symbols.html
Dort gibt es u.a. den Zeichensatz für "Super and Subscripts", allerdings nur für mathematische Symbole.

Gruss
Pulpapex

16.11.2005 - 12:27 Uhr

<?xml version="1.0" standalone="yes"?>

Das muss, wenn angegeben, immer die erste Zeile im Dokument sein.

16.11.2005 - 09:03 Uhr

Probiere es mal statt "string myVar" mit "StringBuilder myVar". StringBuilder ist bei Rückgabe-Parametern notwendig.

Das unsafe kannst du eigentlich weglassen. Du benutzt keinen unsave managed Code. Stören dürfte es aber auch nicht.

Ansonsten scheint es meiner Meinung nach ok zu sein. StdCall ist die default Aufrufkonvention in DllImport. Wie äußert sich das Problem überhaupt?

Gruss
Pulpapex

15.11.2005 - 23:48 Uhr

Hi Crox,

du kannst den IE als Klasse importieren. Dann hast du Zugriff auf alle Eigenschaften und Methoden und kannst z.B. auch die Darstellung anpassen. Toolbar, Addressleiste, Statusleiste ein- und ausblenden und solche Sachen.

Einfach in der IDE einen COM-Verweis zum Projekt aus der Bibliothek %windows\system32\shdocvw.dll hinzufügen. Ausserhalb einer IDE funktioniert das mit dem Type-Library Importertool TlbImp.exe.

Siehe Thread: Prozess/InternetExplorer schliessen

Das Problem mit den doppelten Instanzen löst sich so von selbst, da du dann ein IE-Objekt hast und dort Visible = true/false, Activate usw. aufrufen kannst.

Gruss
Pulpapex

15.11.2005 - 23:33 Uhr

Deswegen nennt man Klassen auch Referenztypen. Es werden nur Referenzen auf Objekte umhergereicht, keine Kopien.

Wenn du eine Kopie brauchst, kannst du z.B. das ICloneable-Interface implementieren oder du fügst deiner Klasse einen Kopier-Konstruktor hinzu. Ein Konstruktor, der ein Objekt des selben Types entgegennimmt und aus diesem die Werte entnimmt, um ein zweites Objekt zu initialisieren.

Du kannst statt einer Klasse auch eine Struktur verwenden. Das wäre dann ein Wertetyp. Wertetypen werden bei einer Zuweisung kopiert. Bei jeder Zuweisung, also wenn es eine grosse Struktur ist, bisschen darauf achten.

15.11.2005 - 20:40 Uhr

Du hast dir doch nicht etwas Sonys Rootkit eingefangen? Ich könnte mir vorstellen, dass das genau sowas macht. Abspielen von Mp3s verhindern oder deinen USB-Stick vorbereiten, damit, falls du Mp3s kopierst, diese auf anderen Rechnern auch nicht abspielen kannst.

Ich weiss, ne sehr weit hergeholte Vermutung. Aber kann man ja mal mit in Betracht ziehen.

15.11.2005 - 20:31 Uhr

Eine Fassade kann man als eine Art Steuerzentrale für ein System sehen.

Sie verbirgt ein grösseres, nicht einfach handhabbares, aber dennoch erforderliches Klassenmodell. Die Verwendung der Funktionen des Klassenmodell wird erleichtert, aber auch eingeschränkt. Es geht Flexibilität verloren.

Mit Vererbung und Interfaces hat das überhaupt nichts zu tun. Im Gegenteil, eine Fassade ist meistens eine fest implementierte, sehr spezielle Klasse. Ihre Schnittstelle ist eine ganz andere als die der Klassen, die sie verbirgt.

14.11.2005 - 23:03 Uhr

Laut Tiobe ist der Anteil der C#-Programmierer mittlerweile auf 3,28% gestiegen (Platz 7 im "Programmiersprachen-Ranking").

http://www.tiobe.com/tpci.htm

Nach der Liste dort könnte auch bald C++ von PHP überholt werden (Platz 3). Andererseits sind die Ergebnisse wahrscheinlich nicht so aussagekräftig, da die Platzierungen anhand von Suchmaschinentreffern ermittelt werden. Und PHP ist nunmal eine Scriptsprache für Webseiten, während C++ mehr in Offline-Anwendungen anzutreffen ist. Aber naja. 😉

14.11.2005 - 22:40 Uhr

Meinst du das hier?

pictureBox.BackColor = Color.Magenta;

Das hat aber nur einen sichtbaren Effekt, wenn das Bild in der PictureBox nicht die gesamte Fläche ausfüllt. ForeColor bringt in so einem Fall auch nichts, die bezieht sich nur auf die Schriftfarbe.

Bleibt noch, die Farbe direkt im Image zu ändern. Dafür müsstest du dich mit der System.Drawing.Graphics-Klasse und dem System.Drawing.Imaging-Namespace auseinandersetzen.

Hier noch ein Weg kurz angedeutet, wie man in einem Bild eine Farbe durch eine andere ersetzen kann. Ich hoffe das Prinzip wird einigermaßen klar, wenn du es dir in der Msdn-Doku angesehen hast:

PictureBox.Image
-> Graphics.FromImage 
-> Graphics.DrawImage 
-> ImageAttributes.SetRemapTable 
-> ColorMap.OldColor/.NewColor

Gruss
Pulpapex

14.11.2005 - 14:17 Uhr

Stimmt,
bei den Meta-Tags fehlt das Tag-Ende.

Dann könnte man noch mit 2 verschiedenen Regexes extrahieren.

@"<XMP>([^<]*)</XMP>" - zum Finden des XMP-Textes (Gruppe 1).
@"\s*\r?\n\s*" - wie gehabt zum Splitten.

14.11.2005 - 13:41 Uhr

Wenn es gültiges Xml ist, würde ich im ersten Schritt die XMP-Elemente als Ganzes auslesen - mit einem XmlReader oder als XmlDocument. Der Text im XMP-Element kann dann mit String.Split oder besser Regex.Split weiter aufgesplittet werden. String.Split macht Probleme, wenn gleiche Trennzeichen aufeinander folgen, z.B. mehrere Leerzeichen.

Split-Regex: @"\s*\r?\n\s*"
Trennt an Zeilenumbrüchen und entfernt gleich führende und nachfolgende Whitespaces.

Gruss Pulpapex

09.11.2005 - 22:53 Uhr

Du musst die Lade-Komponente, die "das Teil" lädt, Ereignisse feuern lassen und du musst ungefähr wissen, wieviele Ereignisse gefeuert werden.

Diese Informationen braucht man, um eine ProgressBar zu aktualisieren. Wenn man die nicht hat, bleibt nur noch eine Pseudo-Fortschrittsanzeige, die z.B. hin- und herläuft.

09.11.2005 - 22:38 Uhr

.. und häufig nicht nachvollziehbar. Manchmal tritt der Effekt auf, manchmal wieder nicht und im Debugger sowieso immer ganz anders.

Also, wenn irgendwie möglich, sollte man auf Threads verzichten. Allerdings, Application.DoEvents ist kein Ersatz. Dann schon lieber Threads und sehr viel testen, dass sichergestellt ist, dass die Vorgänge immer synchron und vorhersehbar ablaufen. Threads, wenn sie auf die selben Daten zugreifen, kann man sich wie in einem rundenbasierten Strategiespiel vorstellen. Wenn ein Thread aktiv ist, müssen die anderen warten.

Das nur so nebenbei. Hat nichts mit dem "GUI <-> Thread Problem" zu tun. Da muss in jedem Fall mit Control.Invoke / Control.BeginInvoke gearbeitet werden.

09.11.2005 - 14:02 Uhr

Wurde doch schon so oft thematisiert.

Nur der GUI-Thread - der Thread, in dem die Forms instanziiert wurden und in dem die Windows-Botschaften-Warteschlage läuft - nur der darf direkt auf Windows-Controls zugreifen. Ansonsten gibt es Probleme. Ein Relikt aus der alten Windows-Programmierung, die nur einen einzelnen Thread erlaubt hat (Single Thread Apartment). Alle Aktionen mit Windows-Controls müssen per Botschaft über die Botschaften-Warteschlange durchgeführt werden.

In .Net 2.0 wird das mittlerweile zur Laufzeit sichergestellt. Es wird eine Exception geworfen, wenn ein anderer Thread auf die GUI zugreift. In .Net 1.1 gab es da nur nicht nachvollziehbare, merkwürdige Effekte, wenn man sich nicht daran hielt.

Ein Thread zum Thema: Threads und Controls

08.11.2005 - 22:31 Uhr

Vielleicht holst du dir doch ein paar Anregungen aus dem Netz.

Ich hab mal auf www.regxlib.com nach URL-Patterns gesucht:
http://www.regxlib.com/Search.aspx?k=url

"(http|https|ftp)://" hab ich dort so gesehen: "(ht|f)tp(s?)://". (Naja, sind nicht ganz identisch).

In den Beiträgen zu den Patterns wird gepostet, welche Fälle nicht beachtet werden.

Gruss
Pulpapex

08.11.2005 - 19:41 Uhr

Also wie Dan schon angedeutet hat, geht das über Reflection.

Du brauchst eine Liste mit den Namen der GUIButton-Felder (stehen in der Xml-Datei?). Dann kommst du auf folgende Weise an die SkinControlAttributes:

FieldInfo button1Info = myForm.GetType().GetField("button1");
SkinControlAttribute attr1 = (SkinControlAttribute)Attribute.GetCustomAttribute(
   button1Info, 
   typeof(SkinControlAttribute));

Du kannst dir natürlich auch alle Felder einer Klasse geben lassen und gucken, welche ein SkinControlAttribute gesetzt haben. Dann braucht man die Feldnamen nicht.

Gruss
Pulpapex

07.11.2005 - 20:16 Uhr

Ein Szenario wo das vorkommen kann:

  • die Methode mit der Schleife kann die Abbruchbedingung nicht selbst initialisieren.
  • die Schleife soll mindestens einmal durchlaufen werden.

Konkretes Szenario:

  • zwei oder mehr Threads synchronisieren sich per WaitHandle.
  • bekommt ein Thread das Signal zum Weiterlaufen, prüft er zunächst eine Bedingung.
  • ist diese nicht erfüllt, kehrt er zurück in den Wartezustand.

Würde die Bedingung gleich zu Beginn geprüft, würde der Thread die Warteanweisung immer überspringen und die anderen Threads würden für immer blockieren.

Gruss
Pulpapex

07.11.2005 - 17:33 Uhr

Na fürs Logging kann man sowas auf jeden Fall gebrauchen. Innerhalb einer Transaktion werden werden viele kleine Transaktionen für Log-Ausgaben durchgeführt. Wenn jetzt die grosse Transaktion schiefläuft, bleiben die Log-Einträge trotzdem erhalten. Wie kann man das anders nachbilden?

In Java ist sowas kein Problem. Dort gibt man der Logging-Methode eine Annotation (ein Attribut) mit, dass sie in einer eigenen Transaktion laufen soll. Eine schon laufende Transaktion wird in der Zeit ausgesetzt und im Anschluss normal fortgesetzt. Beide Transaktionen laufen unabhängig von einander.

Ich hab aber keine Ahnung wie das auf Datenbank-Ebene gelöst ist, also welche Befehle an die Datenbank gesendet werden. Das ist das Problem mit Attributen/Annotationen. Man weiss nicht mehr genau was da eigentlich passiert. Auf jeden Fall soll es auch mit dem MS SQL-Server funktionieren.

Hier steht was davon, dass es deklarative Transaktionen ab Version 2.0 auch in .Net gibt:
http://www.trivadis.ch/Images/basta_transactions_tcm16-12671.pdf

Gruss
Pulpapex

07.11.2005 - 15:17 Uhr

Du suchst wahrscheinlich den is-Operator. Entspricht dem instanceof-Operator von Java.

if(obj is ArrayList) {
}
07.11.2005 - 15:11 Uhr

Wenn ein Typ nicht IComparable implementiert, kann man die Funktionalität auch losgelöst per IComparer bereitstellen.

Du schreibst einfach einen IComparer für den Typ in der Fremdbibliothek und übergibst ihn als zweiten Parameter an die Sort-Methode.

Gruss
Pulpapex

07.11.2005 - 14:54 Uhr

Ich würde als Regex folgenden nehmen:

(?<=Ecu_Name\(S:)[^)]*

(?≤Ecu_Name(S:) ist ein positiver Lookbehind:

  • eine Gruppe, die auf ("Ecu_NameS:" matcht, die aber nicht im Ergebnis auftaucht.

([^)]*){blue} greift sich alles, was keine "runde Klammer zu" ist.

Die Matches des Regex sind genau die Strings, die du haben möchtest. Also "SAMH164", "KL164" und "KIXAGW164".

private static Regex ecuNameRegex = new Regex(
   @"(?<=Ecu_Name\\(S:)[^)]*", 
   RegexOptions.Compiled);

private List GetEcuNames(string text) {
   List ecuNames = new ArrayList();
   foreach(Match m in ecuNameRegex.Matches(text)) {
      ecuNames.Add(m.Value);
   }
   return ecuNames;
}

06.11.2005 - 05:26 Uhr

Division bei Ganzzahl-Typen ist auch immer eine Ganzzahldivision.

06.11.2005 - 05:20 Uhr

die Umwandlung zu sbyte würde ich weglassen. Bringt nichts.

Die heutigen 32 Bit Rechner kommen halt besser mit 32 Bit zurecht. Alle Zwischenergebnisse der Rechnung sind 32 bit. Der cast bedeutet nur zusätzlichen Aufwand. Und Speicher sparst du dadurch auch nicht. Gerade mal 3 Byte. Der Cast nach sbyte benötigt wahrscheinlich mehr als 3 Byte IL-Code. Selbst ein bool belegt intern 32 bit.

Bei einem grossen Array könnte man vielleicht nochmal überlegen.

Gruss
Pulpapex.

05.11.2005 - 21:18 Uhr

Es wird alles übertagen was sich mit dem Button in der selben Html-Form befindet. Du könntest die Webseite in zwei Forms aufteilen. Dann werden nur die Daten der Form übertragen, die submitted wird.

Gruss
Pulpapex

05.11.2005 - 20:05 Uhr

Per Klasse oder Struct, Methoden können nur einen Rückgabewert haben.

public struct DarstellungInfo {
   public string Name;
   public string L;
   public string D;
   public string B;
}

Gruss
Pulpapex

04.11.2005 - 22:32 Uhr

Mit der deutschen Wikipedia-Version von MVC stimme ich überhaupt nicht überein. Dort implementiert der Controller die Anwendungslogik. Das Model hält nur die Daten.

Diese Trennung von Logik und Daten macht keinen Sinn. Ich hatte das schon mal hier irgendwo erläutert. Was nutzt einem die Logik im Controller? Der Controller kennt die View! Wenn man eine schlanke Kommandozeilen-Version für sein GUI-Programm anbieten möchte, geht das nicht. Man muss immer die fette GUI mit ausliefern, weil die Anwendungslogik von ihr abhängt.

Mein Beispiel sieht ganz anders aus. Dort stellt der Controller nur Glue-Code ohne eigene Logik da. Er konfiguriert Model und View, reagiert auf deren Ereignisse und delegiert sie als Aktionen weiter (Closing-Event -> Login -> userManager.Login -> e.Cancel).

Möchte man Model oder View ersetzen, muss man auch immer den Controller mit ausgetauschen. Also die App.exe muss neu geschrieben werden. Ziel ist es deshalb den Code im Controller möglichst klein und einfach zu halten, damit der Austausch nicht zu viel Aufwand macht.

herbivore, in deinem Diagamm ist nicht zu erkennen, dass die Benutzer-Interaktionen eigentlich über Ereignisse der View mitgeteilt werden: okButton.Click - der Benutzer hat den Schalter gedrückt. Auch sieht es so aus, als würde das Model die View kennen. Das liegt aber nur an der Darstellung, die sehr von der Programmierersicht abstrahiert. Eigentlich soll dein und mein Diagramm genau das selbe darstellen. Der Controller reagiert auf Ereignisse und bedient das Model. Bei mir bedient er noch die View, z.B. öffnet er Dialoge oder schaltet Sichten um. Model-Aktualisierungen kommen auch bei mir, wenn möglich direkt ohne Umweg über den Controller zur View, per Event oder bound Properties.

Gruss
Pulpapex

04.11.2005 - 15:24 Uhr

Hi michaelschuler,

was du da beschreibst, ist in etwa der normale, saubere MVC-Ansatz.

Es gibt Views,

  • "dumme" Windows-Controls/Forms, in die der Benutzer was eingibt.

Model-Klassen,

  • Datenhaltung und Anwendungslogik.

Und es gibt Controller-Klassen

  • die eigentliche Anwendung (aber hat enthält keine/wenig Programmlogik).
  • Sie verwendet die Model-Klassen und Views.
  • Sie verknüpfen und initialisieren das Ganze.

Beispiel - Benutzer einloggen.

// GUI-Komponente, kennt nur sich selbst.
public class LoginDialog : Form {
   public string User { get; set; }
   public string Password { get; set; }
   public string LoginErrorMsg { get; set; }
}
// Model-Klasse, kennt nur sich selbst.
public class UserManager {
   public bool Login(string user, string password);
   public string LoginErrorMsg { get; }
}
// Controller (Anwendung), verwendet LoginDialog und UserManager.
public class App {

   private bool loggedIn = false;
   private LoginDialog loginDialog;
   private UserManager userManager;

   public static void Main() {
      new App().Login();
   }

   public App() {

      loginDialog = new LoginDialog();
      loginDialog.Closing += new CancelEventHandler(HandleClosing);

      userManager = new UserManager();
   }

   public void Login() {
      loginDialog.ShowDialog();
      Console.WriteLine("Logged In: {0}", loggedIn);
   }

   private void HandleClosing(object sender, CancelEventArgs e) {
      e.Cancel = !DoLogin();
   }

   private bool DoLogin() {

      // (Edit: hab ich aus dem HandleClosing rausgezogen. 
      // Ist hier besser aufgehoben).
      if(loginDialog.DialogResult != DialogResult.Ok) {
         loggedIn = false
         return true; // e.Cancel = false;
      }

      loggedIn = userManager.Login(loginDialog.User, loginDialog.Password);
      if(!loggedIn) {
          // Kann man auch über ein "bound property" realisieren.
          userDialog.LoginErrorMsg = userManager.LoginErrorMsg;
      }
      return loggedIn;
   }

}

(Hier werden wahrscheinlich Syntaxfehler drin sein, hab's nur schnell geschrieben. Geht nur ums Prinzip)

Im Anhang das Ganze nochmal als Diagramm. Gestrichelte Linien stellen Ereignisse da. Die durchgezogenen Linien sind die Assoziationen/Abhängigkeiten.

Gruss
Pulpapex

04.11.2005 - 12:56 Uhr

Wo kommt dieser Lange String überhaupt her?

Das sind irgendwelche Text- oder Binärdaten, die Base64-kodiert per Http übertragen werden. Ich hab nicht so viel Ahnung von ASP.NET. Wenn ich die Doku richtig deute, dann ist das der Inhalt der Formularfelder auf der Webseite. Der Browser schickt sie in einem Hidden-Field namens ViewState per POST an die Server-Anwendung. (Find ich ein bisschen daneben, wenn das wirklich so gehandhabt wird, aber naja.)

Du kannst den String ja mal manuell dekodieren, um zu sehen was drin steht. Wie es geht, steht in der Fehlermeldung. Wenn sie erneut auftaucht, ein '=' an den String anhängen und es nochmal probieren, usw. bis es klappt.

04.11.2005 - 01:14 Uhr

Hallo,

vielleicht hilft dir die Wikipedia-Seite zur Base64-Kodierung weiter.

Die Gesamtzahl kodierter Bytes muss durch 3 teilbar sein. Die Anzahl der in der Kodierung verwendeten Zeichen muss durch 4 teilbar sein. Wenn es nicht passt, wird mit '=' aufgefüllt.

dDwxMTI3MTUxNjAxO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDE+O2k8Mj47aTwzPjtpPDQ+Oz47bDx
0PHA8cDxsPFZpc2libGU7PjtsPG88Zj47Pj47Pjs7Pjt0PHA8cDxsPFZpc2libGU7PjtsPG88Zj
47Pj47Pjs7Pjt0PHA8cDxsPFZpc2libGU7PjtsPG88dD47Pj47PjtsPGk8MT47PjtsPHQ8dDw7d
DxpPDMzPjtAPFRpc2NoIDE7VGlzY2ggMjtUaXNjaCAzO1Rpc2NoIDQ7VGlzY2ggNTtUaXNjaCA2
O1Rpc2NoIDc7VGlzY2ggODtUaXNjaCA5O1Rpc2NoIDEwO1Rpc2NoIDExO1Rpc2NoIDEyO1Rpc2N
oIDEzO1Rpc2NoIDE0O1Rpc2NoIDE1O1Rpc2NoIDE2O1Rpc2NoIDE3O1Rpc2NoIDE4O1Rpc2NoID
E5O1Rpc2NoIDIwO1Rpc2NoIDIxO1Rpc2NoIDIyO1Rpc2NoIDIzO1Rpc2NoIDI0O1Rpc2NoIDI1O
1Rpc2NoIDI2O1Rpc2NoIDI3O1Rpc2NoIDI4O1Rpc2NoIDI5O1Rpc2NoIDMwO1Rpc2NoIDMxO1Rp
c2NoIDMyO1Rpc2NoIDMzOz47QDxUaXNjaCAxO1Rpc2NoIDI7VGlzY2ggMztUaXNjaCA0O1Rpc2N
oIDU7VGlzY2ggNjtUaXNjaCA3O1Rpc2NoIDg7VGlzY2ggOTtUaXNjaCAxMDtUaXNjaCAxMTtUaX
NjaCAxMjtUaXNjaCAxMztUaXNjaCAxNDtUaXNjaCAxNTtUaXNjaCAxNjtUaXNjaCAxNztUaXNja
CAxODtUaXNjaCAxOTtUaXNjaCAyMDtUaXNjaCAyMTtUaXNjaCAyMjtUaXNjaCAyMztUaXNjaCAy
NDtUaXNjaCAyNTtUaXNjaCAyNjtUaXNjaCAyNztUaXNjaCAyODtUaXNjaCAyOTtUaXNjaCAzMDt
UaXNjaCAzMTtUaXNjaCAzMjtUaXNjaCAzMzs+Pjs+Ozs+Oz4+O3Q8cDxwPGw8VmlzaWJsZTs+O2
w8bzxmPjs+Pjs+O2w8aTwxPjtpPDM+O2k8NT47aTw3PjtpPDk+O2k8MTM+O2k8MTk+O2k8MjU+O
z47bDx0PHA8cDxsPFRleHQ7PjtsPEJhcmNvZGU6IDEwMDQ4Oz4+Oz47Oz47dDxwPHA8bDxUZXh0
O1Zpc2libGU7PjtsPDEwMDQ4O288Zj47Pj47Pjs7Pjt0PHA8cDxsPFZpc2libGU7PjtsPG88Zj4
7Pj47Pjs7Pjt0PHQ8O3Q8aTw4PjtAPENvY2EgQ29sYSAgMCwzM2wgIDAsNTAg4oKsIDtGYW50YS
BaaXRyb25lIDAsNTBsICAxLDAwIOKCrCA7SGFzc2Vyw7ZkZXIgMCw1MGwgIDIsMDAg4oKsIDtLc
m9tYmFjaGVyIDAsNTBsICAzLDMzIOKCrCA7U3ByaXRlIDAsNTBsICAxLDAwIOKCrCA7S2FmZmVl
IDAsMjBsICAxLDAwIOKCrCA7TGF0dGUgTWFjY2hpYXRvIDAsMzNsICAxLDAwIOKCrCA7V2Fzc2V
yIDAsMjBsICAwLDUwIOKCrCA7PjtAPENvY2EgQ29sYSAgMCwzM2wgIDAsNTAg4oKsIDtGYW50YS
BaaXRyb25lIDAsNTBsICAxLDAwIOKCrCA7SGFzc2Vyw7ZkZXIgMCw1MGwgIDIsMDAg4oKsIDtLc
m9tYmFjaGVyIDAsNTBsICAzLDMzIOKCrCA7U3ByaXRlIDAsNTBsICAxLDAwIOKCrCA7S2FmZmVl
IDAsMjBsICAxLDAwIOKCrCA7TGF0dGUgTWFjY2hpYXRvIDAsMzNsICAxLDAwIOKCrCA7V2Fzc2V
yIDAsMjBsICAwLDUwIOKCrCA7Pj47bDxpPDA+Oz4+Ozs+O3Q8dDw7O2w8aTwwPjs+Pjs7Pjt0PH
Q8O3Q8aTwxPjtAPDAzIHggMCw1MOKCrCAwLDMzbCBDb2NhIENvbGEgOz47QDwwMyB4IDAsNTDig
qwgMCwzM2wgQ29jYSBDb2xhIDs+Pjs+Ozs+O3Q8cDxwPGw8VmlzaWJsZTs+O

Hier sind es 75 Zeichen pro Zeile mal 28 Zeilen. In der letzten Zeile sind es nochmal 60 Zeichen. Das bedeutet (75*28 + 60) % 4 = 0. Es ist also teilbar. Die Zeilenumbrüche werden bei der Kodierung eingefügt, ansonsten aber ignoriert.

Scheint korrekt zu sein. Wenn es nur auf dem PDA Probleme macht, würde ich einen Bug oder irgend einen Seiteneffekt vermuten.

Gruss
Pulpapex

03.11.2005 - 02:25 Uhr

Es gibt Vorteile, aber auch Nachteile, OOP macht nicht immer Sinn. Wenn es beispielsweise wenig Logik und wenig Datenstruktur, dafür aber viele Daten gibt und es auf Geschwindigkeit ankommt, ist Objekt-Orientierung fehl am Platz. Das trifft z.B. auf Datenbank-Massendaten, DSP oder Shader-Programmierung zu.

In deiner Anwendung scheint mir Objektorientierung jedoch hilfreich zu sein, da es eine komplexe Datenstruktur gibt.

Bei OOP geht es ganz allgemein um die Abbildung eines Teils der realen Welt auf Klassen - auf ein sogenanntes Objektmodell. Das Objektmodell ist unabhängig von der Art der Anwendung (Web, Gui, Konsole ...) konzipiert, in der es verwendet wird. Es enthält nur Daten und definiert Operationen auf diesen. Das ganze sollte modular gehalten sein. D.h. das Objektmodell braucht nicht zu wissen, dass es in einer Datenbank gespeichert wird, es gibt einen Datenbank-Service, dem man es übergeben kann. Genauso wenig muss es wissen, dass es eine GUI gibt, die es anzeigt oder eine Druck-Komponente, die es formatiert und ausdruckt, - einen Web-Server, einen Berichtsgenerator usw. Da das Objektmodell so einfach und allgemein gehalten ist. Insbesondere, da es nicht innerhalb der GUI-Klassen implementiert ist, können leicht neue Services hinzuimplementiert werden und es kann in anderen Umgebungen verwendet werden, Ausdrucken per Kommandozeile beispielsweise.

Deine Anwendung liesse sich sauber trennen in App.exe, Core.dll, Gui.dll, und Db.ll. App.exe kennt alle drei Dlls. Core.dll mit dem Objektmodell kennt nur sich selbst. Gui.dll kennt u.U. Core.dll (obwohl es auch möglich ist, die Gui unabhängig vom Objektmodell zu implementieren). Und Db.dll kennt Core.dll.

So stehen einem alle Möglichkeiten offen. Wie schon oben geschrieben, kann man nun Core.dll und Db.dll nehmen, um Webseiten zu rendern oder auszudrucken. Genauso gut ist es auch möglich, Db.dll durch eine WebService.dll ersetzen, um Objektmodell-Daten bei einem WebService anzufordern.

Aber Flexibität und Modularität muss man sich meistens mit mehr Code erkaufen, das stimmt schon. Durch die einfachere, aufgeräumte Schnittstelle und durch die leichtere Wiederverwendbarkeit sollte im Endeffekt der aber Aufwand sinken, hoffentlich.

Gruss
Pulpapex

02.11.2005 - 23:32 Uhr

Gibt es,

das grundlegende Prinzip hierfür nennt sich Serialisierung. Damit ein Objekt serialisiert werden kann muss die Klasse und alle referenzierten Typen das Serializable-Attribut gesetzt (ist bei den meisten .Net-Klassen der Fall). Alternativ kann man auch das ISerializable-Interface implementieren, das ist dann benutzerdefinierte Serialisierung.

Im .Net-Framework wird ein binärer Serialisierer und ein Serialisierer auf Xml-Basis mitgeliefert. Das nur zur Einführung. Ich hoffe du kommst damit weiter.

Gruss
Pulpapex

01.11.2005 - 20:59 Uhr

Nimm doch einfach einen fertigen Logger. Z.B. die log4j-Portierung Apache log4net.

Benutzt wird log4net so:

public class MyClass {

   // Statische Logger-Instanzen.
   private static ILog log = LogManager.GetLogger(typeof(MyClass));
   private static ILog mailer = LogManager.GetLogger("mailer");

   // Methode, die die Loggers benutzt.
   public void DoSomething() {

      try {
         log.Debug("Debug");

      } catch(Exception ex) {
         log.Error(ex);
      }

      if(seriousError) {
         mailer.Fatal("Serious Error");
      }
   }
}

Die Konfiguration der Logger erfolgt entweder irgendwo bei Programmstart oder wird in einer Xml-Config-Datei hinterlegt. Statt der zwei Logger log und mailer könnte man die Konfiguration auch so anpassen, dass z.B. alles standardmässig in eine Datei geloggt wird, aber ab Stufe fatal auch Mails versendet werden.

Geloggt wird in die sogenannten Appenders - Log-Ziele. Das kann alles mögliche sein, z.B. Mail, Datenbank, Datei, Console, Application Event Log usw. (siehe: log4net-Features). Was genau geloggt wird - Datum, Uhrzeit, Quelle, Meldung usw. - wird in der Appender-Konfiguration per Format-String festgelegt.

@herbivore: am Code-Beispiel sieht man auch den Vorteil von Singletons, gegenüber Klassen, deren Methoden komplett statisch sind. Mit Singletons kann es mehrere Logger-Implementierungen geben und die Anwendung muss nicht wissen welchen sie verwendet. Mit statischen Methoden bräuchte man z.B. einen FileLogger und einen MailLogger, die fest in der Anwendung verdrahtet sind. Auch wäre es nicht möglich zwei verschieden konfigurierte FileLoggers gleichzeitig zu verwenden, falls das mal notwendig sein sollte.

Gruss
Pulpapex

28.10.2005 - 01:06 Uhr

Wie meinst du das "die Struktur der Objekte nicht ablegen"? Ein O/R-Mapper macht genau das, er bildet einzelne Klassen auf eine oder mehrere Datenbanktabellen ab. Desweiteren mappt er die Beziehungen zwischen den Klassen: Vererbungsbeziehungen, Assoziationen, 1:n/m:n-Beziehungen (Collections) und noch ein paar andere.

Um die Änderungen eines kompletten Objektgraphen mit (N)Hibernate zu speichern, reicht:

ISerializable id = session.Save(objectGraph);

Um Objektgraph wieder zu laden reicht:

ObjectGraph objectGraph = (ObjectGraph)session.Load(typeof(ObjectGraph), id);

Hinzu kommt die einfache, aber mächtige Anfragesprache HQL. Z.B. kann man sowas schreiben:


// Mapping
// class: Parent, subclasses: Mother, Father
// class: Child

// Alle Mütter, die ein Kind älter als 15 haben.
IList mothers = session.Find(
   "select m from Mother m
   join m.children c
   where c.age > 15");

// Die selbe Anfrage aus entgegengesetzter Richtung.
IList mothers = session.Find(
   "select c.mother from Child c
   where c.age > 15");

HQL-Anfragen werden im Hintergrund in den eingestellten SQL-Dialekt übersetzt. Zumindest Hibernate kommt mit sehr vielen Datenbanksystemen zurecht (bei NHibernate weiss ich es nicht). Man braucht sich also nicht einmal mehr um die verschiedenen SQL-Dialekte zu kümmern.

Ans selber Schreiben solltest du gar nicht erst denken, kann nur schiefgehen. Die Frage ist, ob ein Mapper eingesetzt werden soll oder nicht.

Vorteile:

  • weniger Code,
  • einfacher, übersichtlicher und konsistent.
  • leicht wartbar.
  • läuft (wenn getestet) ohne Anpassung auf verschiedenen Datenbanken.

Nachteile:

  • langsamer als natives SQL.
  • Zwischenschicht, die die Fehlersuche erschwert (sehr viel Magie).
  • Mapping-Definition bei vorgegebenem DB-Schema kann sich schwierig gestalten.
  • In NHibernate: keine Bulk-Updates. Die kann man aber über die Verbindung von Hibernate direkt an die Datenbank absetzen, wirkt sich nur nicht auf die bereits geladenen Objekte aus.
27.10.2005 - 01:36 Uhr

"eco[name]" findet nicht "eco" und auch nicht "eco_name", sondern z.B. "eco".

@jofenchel: neben dem was herbivore geschrieben hat, irritiert mich noch das "+ ecu".

Ganz allgemein; Regexes dynamisch zusammen zu bauen, ist keine gute Idee, ist zu rechenaufwändig. Poste mal paar Beispielzeilen und dazu welche wie gematcht werden sollen und welche nicht (den Regex nicht im CSharp-Codeblock schreiben, der verschluckt die Backslashes).

27.10.2005 - 01:16 Uhr

Hi Rainbird,

so in der Art habe ich das MVC-Pattern auch verstanden.

Konkret zum Controller:
Normalerweise registriert der Controller die View für Model-Ereignisse und umgekehrt (oder er verbindet Daten-bindungsfähige Properties). Ansonsten hat er nicht viel zu tun. Er kann sich auch selbst für die Ereignisse registrieren, wenn ein direktes Verbinden von Model und View nicht möglich ist. Der Code im Ereignishandler sollte dann allerdings so kurz wie möglich sein. Auf keinen Fall darf der Contoller selbst auf Ereignisse reagieren. Er darf nur sehr wenig Logik enthalten.

Für mich ist der Controller nur notwendiger Glue-Code. Wird die View oder das Model wiederverwendet, muss der Controller in jedem Fall neu geschrieben werden.

In der Wikipedia ist das, finde ich, falsch beschrieben. Dort enthält der Controller alle Anwendungslogik. Das Model enthält nur die Daten, aber nicht keine Operationen auf diesen, wie es eigentlich sein sollte.

Wikipedia: Model View Controller

Controller

Die Steuerungsschicht realisiert die eigentliche Geschäftsintelligenz und bildet mit Hilfe der anderen Schichten die Prozesse ab. Sie steuert den Ablauf, verarbeitet Daten, entscheidet, welche View aufgerufen wird, etc.

An dieser Stelle driften die Ansichten auseinander. Aber es muss eigentlich falsch sein, ... wenn der Controller die Logik enthält und dazu noch die View kennt, kann die View nicht ohne weiteres ausgetauscht werden. Der Controller kommt ohne die View nicht aus. Das bedeutet, dass alle drei Komponenten eng verbunden sind, was MVC ja gerade verhindern soll.

Also eigentlich müsste der Artikel angepasst werden. Oder gibt es noch andere Sichtweisen?

Gruss
Pulpapex

26.10.2005 - 08:15 Uhr

Die Fehlermeldung sagt, entweder es fehlt eine using-Direktive oder die Assembly mit der gesuchten WebBrowser-Klasse wird nicht referenziert. In beiden Fällen kann der Type nicht aufgelöst werden.

Compiler Error CS0246

Kann es sein, dass du mit .Net 1.1 programmierst? Da gibt es noch kein WebBrowser Windows-Control.

25.10.2005 - 08:18 Uhr

Ich würde sagen es kommt drauf an was man als Ergebnis haben möchte. Geht es um die Anzeige der neuen Seite in einem Fenster, ist das WebBrowser-Control besser geeignet. Wenn man die Daten haben möchte, ist es meiner Meinung nach besser mit einem WebClient oder HttpWebRequest den Post- oder Get-Aufruf selbst zu senden. Was anderes macht der Button auch nicht.

Nur der Vollständigkeit halber, das WebBrowser-Control ist der IE in einer COM-Komponente. Das gleiche gibt es auch für Mozilla.

Mozilla ActiveX Control

19.10.2005 - 00:49 Uhr

Bakunin
Was ich jetzt nicht ganz verstehe, wieso diese Schritte durchmachen.

Die Schritte darf man nicht so eng sehen. Das sind Beispiele, die erläutern sollen, wie testgetriebene Entwicklung (und Extreme Programming) vom Prinzip her ablaufen.

  1. Schritt, die nicht implementierte Eigenschaft sollte im Test fehlschlagen.
  2. Schritt, die extremst einfachste Implementierung funktioniert anfangs. Super, reicht erstmal so.
  3. Schritt, man programmiert weiter und lässt zwischendurch seine bis dahin entwickelten Tests laufen. Einige Tests werden fehl schlagen. So sieht man sofort, welcher alter Code mit den aktuellen Änderungen nicht mehr zusammen funktioniert und angepasst werden muss. Die in Schritt 2 entwickelte, einfachste Implementierung muss wahrscheinlich etwas ausgebaut werden.

Ziel ist es, so einfachen und primitiven Code wie möglich zu schreiben, der gerade das erledigt, was er soll. Vorkehrungen für etwaiige, zukünftige Anforderungen werden rigoros ausser Acht gelassen. Man kennt sie ohnehin nicht. Ansonsten steckt man viel Aufwand in etwas was später in der Form niemals erforderlich sein wird. Da man aber den Quatsch nicht wieder entfernen möchte, war ja viel Arbeit, versucht man alle notwendigen Änderungen ins geschaffene "Framework" zu integrieren. So steigt der Aufwand für Änderungen langsam ins Unermessliche.

Die einfache Lösung zu programmieren, ist manchmal schwieriger als man denkt. Man muss sich dazu zwingen, pragmatisch und zielgerichtet zu programmieren (und auch viel umzustricken). Am Anfang hat man tolle Ideen, die alle umgesetzt werden müssen. Am besten gleich alle auf einmal. Das Programm, das alles kann! Von dieser Vorstellung muss man sich lösen. Die Beispiele sind deshalb so krass gewählt, damit man sich mehr zusammenreisst und zur einfachsten Lösung für ein Problem strebt. Wenn es konkret erforderlich wird, kann immer noch erweitert werden.

Das ging jetzt eher in Richtung XP als in Richtung Testfälle.
Zu den Testfällen. Ich finde es ist nicht unbedingt notwendig, dass jede einzelne Methode oder Eigenschaft getestet wird. Zu viele Tests sind auch nicht gut, da sie sonst zu häufig angepasst werden müssen. Meiner Meinung nach schreibt man Tests pro Testfall/Szenario, so wie larslovesdotnet es beschrieben hat. Auf der anderen Seite kann das natürlich auch bedeuten, dass man gleich mehrere Tests für eine einzelne Methode/Eigenschaft zu schreiben hat. Da man dabei aber gefordertes Verhalten testet, sollten die Tests einigermassen stabil bleiben.

Gruss
Pulpapex

16.10.2005 - 04:34 Uhr

Thread.Sleep oder WaitHandles sind überhaupt nicht notwendig. Das Lesen des Sockets blockiert schon ohne Zutun die Threadausführung, wenn keine Daten gesendet werden. Die Schleife ist nur dafür da, um blockweise Daten zu empfangen und mitzubekommen, wenn der Client die Verbindung schliesst.

Thread.Sleep solltest du wieder ausbauen. Das verlangsamt nur künstlich die Ausführung.

15.10.2005 - 04:35 Uhr

Compare ist hauptsächlich fürs Suchen und Sortieren interessant. Zum Vergleichen reicht Equals oder =.

-1 bedeutet, dass der erste String-Parameter lexikalisch kleiner ist als der zweite.
0 bedeutet, gleiche Strings
1 bedeutet, String 1 ist grösser als String 2.

Beispiel: "Anton" ist kleiner "Berta", Compare liefert Wert < 0.

Siehe auch IComparer-Interface. Das Interface vergleicht allgemein Objekte, nicht nur Strings.

Gruss
Pulpapex

15.10.2005 - 04:11 Uhr

Bisschen Code wäre hilfreich gewesen.

Ich würde vermuten, dass dein Thread ohne Schleife ausgeführt wird. Ein Thread wird beendet, sobald die als ThreadStart oder ähnlich übergebene Methode zurückkehrt. Du brauchst eine Schleife, die solange läuft, bis der Client die Verbindung schliesst.

Den Verbindungsaufbau würde ich auch in den Thread verlagern. AcceptSocket oder AcceptTcpClient blockieren ansonsten genauso den Rest der Anwendung.

Gruss
Pulpapex

14.10.2005 - 15:15 Uhr

Hi seth,

ich würde es wahrscheinlich so machen:

Alle Spieler-Objekte befinden sich in einer Liste, die nach dem Punktestand sortiert ist. Zum Sortieren kann man ArrayList.Sort verwenden. Als Parameter übergibt man einen selbst geschriebenen PunktestandComparer, der Spieler nach ihrer Punktestand-Eigenschaft vergleicht.

Der Index eines Spielers in der Liste entspricht dann seiner Platzierung.

Um einen Spieler mit einem gewissen Punktestand einzufügen, kann man ArrayList.BinarySearch zusammen mit dem PunktestandComparer verwenden. Als Ergebnis wird der Index zurückgegeben, an dem der Spieler eingefügt werden muss. Die Methode kann auch zur Ermittlung der Platzierung verwendet werden (oder jeder Spieler speichert seine Platzierung in einer Eigenschaft).

Gruss
Pulpapex