Laden...

Forenbeiträge von N1ls Ingesamt 46 Beiträge

14.11.2010 - 22:01 Uhr

@N1ls: Was genau meinst du? Das mit dem statischen "Create" oder dass die Syntax dafür möglichst kurz sein soll? Für das "Create" habe ich jedenfalls einen praktischen Nutzen, ist allerdings etwas aufwändiger zum erklären. Und Delphi kann ich auf jeden Fall mal nicht^^

Ich meine eher das statische Create. Kurz wird's ja schon, wenn ich nur


A myA = new A()

schreibe.

Konstruktoren sind eh statisch. Also wieso diese Umständlichkeiten?

Grüsse,

N1ls

11.11.2010 - 23:25 Uhr

Hallo,

gibt es für soetwas irgendeinen praktischen Nutzen? Tschuldigung, dass ich nachfrage. Aber für mich sieht das irgendwie so aus, als ob Du C# eine Delphi-Syntax aufzwingen willst...

Grüsse,

N1ls

09.10.2010 - 23:48 Uhr

Hallo Herbivore,

Hallo TheBrainiac,

deine Lösung ist korrekt.
...

Hier zum Vergleich noch, wie ich mir das vorgestellt hatte.
...

Ich halte beide Lösungen für relativ schwer lesbar. Wirklich korrekt sind sie auch nicht. Ein Kniffel ist immer auch ein FullHouse, das berücksichtigen beide Varianten nicht. Das lässt sich natürlich einfach korrigieren, steigert aber die Verständlichkeit des Codes nicht wirklich.

MfG,

N1ls

01.09.2010 - 09:01 Uhr

Das ist Unsinn. StringBuilder dient bei Plattformaufrufen als beschreibbarer Puffer. [...]
Wenn du eine konstante Zeichenkette als Parameter übergibst oder als Rückgabewert bekommst kann dieser als string deklariert werden.

Wenn das so unsinnig ist, wäre es ja schön gewesen, Du hättest auch mal ein entsprechendes Beispiel mit einem String-Rückgabewert aufgeführt.

MfG,

N1ls

30.08.2010 - 21:40 Uhr

Hallo And.Wolf,

[DllImport("mydll.dll", SetLastError = true,CharSet = CharSet.Ansi)]
private static extern StringBuilder MyFunction(string Par);

versuch es mal so. Hab's jetzt nicht ausprobiert. Ich weiss auf jeden Fall, dass Du einmal den Stringbuilder für den Rückgabewert nutzen musst, einen einfachen String für den Übergabeparameter. Klingt erstmal komisch, ist aber so 😉

Evtl. musst Du noch die Calling Convention im Delphi Code auf StdCall anpassen.

Grüsse,

N1ls

26.08.2010 - 22:00 Uhr

Hallo CSL,

Wenn man erst die UI macht besteht aber die Gefahr das man u.u:

  • "mal eben" eine Funktionalität ein hackt,
  • das man UnitTests weg lässt um die UI "schnell mal eben" mit Funktionalität aus zu statten,
  • das man weniger Abstrahiert sondern direkt mit der UI verknüpft (Ruf die Komponente einfach mal schnell auf()

Da gebe ich Dir vollkommen Recht. Dazu gehört dann natürlich Disziplin und sicherlich auch Überzeugungsarbeit bei so manchem Chef, um gegen diese Gefahren zu arbeiten. Aber das ist doch auch ein Stück, worauf man hinaus will, wenn man Test-Driven entwickeln als sinnvoll ansieht, oder?
Das UI steht, es ist bekannt welche TextEdits es gibt, welche Checkboxen, Radiobuttons etc. Jetzt kann man an die Logik des UI gehen und diese Test-Driven entwickeln - quasi per Unit-Tests Stück für Stück das Klasse aufbauen, die die Logik des UI widerspiegelt.

  • das man Dummy Templates erstellt die die Funktionalität erst viel Später bekommen (wichtige Zeit verloren),

Man muss ja nicht zwangsweise das komplette UI sofort erstellen. Wenn sich Teile sinnvoll abgrenzen lassen, kann man da sicherlich schrittweise vorgehen.

  • und zu guter letzt das man die UI immer wieder umbauen muss da sich bei der Implementation der Funktionalität neue Erkenntnisse ergaben.

Genau das bezweifel ich. Das UI entwickele ich nach den Anforderungen und Wünschen des Kunden. Wenn dieser damit zufrieden ist, dann interessiert ihn nicht mehr, was dahinter liegt. Wenn bei der Implementierung der Funktionalitäten dann Probleme auftauchen, heisst das entsprechend, ich kann Anforderungen respektive Wünschen nicht mehr gerecht werden. Dann habe ich aber ein ganz anderes Problem als den Umbau des UI. Das sollte aber der Ausnahmefall sein.

Grüsse,

N1ls

26.08.2010 - 20:37 Uhr

Hallo herbivore,

Alleine die Auffassung "nur noch eine UI davor packen" widerspricht jeglichen agilen Prinzipien. Und TDD kommt nun einmal aus genau dieser Richtung.
TDD sagt erstmal nichts darüber aus, ob man Top-Down oder Bottom-Up entwickelt. Es sagt nur, dass du den Test vor der Implementierung schreibst.

Ich habe nicht behauptet, dass TDD eine Richtung vorgibt. Es kommt aber aus der agilen Softwareentwicklung und dort geht es u.A. darum kontinuierlich Nutzen zu produzieren. Das kann ich eben nicht, wenn ich zuerst mit TDD das komplette Backend fertige und dann das UI anfange. Im schlimmsten Falle ist Release-Termin, wenn ich mit dem Backend gerade durch bin... das heisst dann, ich habe noch überhaupt keine Funktionalität geschaffen, jedenfalls keine für den Kunden nutzbare.

Gerade wenn du sagst, dass bei der Entwicklung "vom User Interface in Richtung Backend" "seltener nachträgliche Änderungen gewünscht werden", bedeutet das doch, dass das das gewünschte Verhalten (gerade auf den oberen, gui-nahen Ebenen) von Anfang an feststeht (und sich nicht mehr ändert). Das sind doch optimale Voraussetzungen, um dieses Verhalten in Forms von Tests auszudrücken und damit auch zu fixieren. Also nochmal, nur weil du TDD verwendest, musst du nicht Bottom-Up arbeiten.

Das ist ja genau meine Meinung. Und so gehe ich bzw. versuche ich möglichst vorzugehen. Deshalb finde ich es auch Schade in Bezug auf TDD etwas zu lesen wie

Man löst Probleme auch von der anderen Richtung, ich für mein Fall hatte oft mit der UI angefangen und dann stück für stück zur Funktionalität vor gearbeitet. Jetzt erstell ich erst die Funktionalität und muss dann nur noch eine UI davor packen.

Bei mir hat sich dieses Bewusstsein, immer vom UI aus zu arbeiten, eigentlich letztes Jahr auf der Advanced Developers Conference gefestigt. In bestimmt 4 oder 5 der von mir besuchten Sessions wurde genau dieses Vorgehen empfohlen, ebenso im anschliessenden Workshop mit Ralf Westphal. Gegenteilige Meinungen habe ich nicht mitbekommen.
Genau daran denke ich jetzt immer, wenn ich von Kollegen höre "das können wir jetzt nicht so einfach ändern, dann müsste ich das ganze Backend umstellen". Dieser Satz taucht immer auf, wenn man dem Kunden zum ersten Mal das UI vorstellt...

Grüsse,

N1ls

25.08.2010 - 23:15 Uhr

Man löst Probleme auch von der anderen Richtung, ich für mein Fall hatte oft mit der UI angefangen und dann stück für stück zur Funktionalität vor gearbeitet. Jetzt erstell ich erst die Funktionalität und muss dann nur noch eine UI davor packen.

Wenn TDD zu einem solchen Verhalten führt, dann werden dadurch natürlich erhebliche Mehrkosten verursacht. Entwicklung vom User Interface in Richtung Backend ist effizient, weil
*keine Funktionalität implementiert wird, die nicht benötigt wird *seltener nachträgliche Änderungen gewünscht werden(wenn das UI vom Kunden abgenommen ist, man im Backend relativ frei)

Alleine die Auffassung "nur noch eine UI davor packen" widerspricht jeglichen agilen Prinzipien. Und TDD kommt nun einmal aus genau dieser Richtung.

Von daher finde ich es natürlich falsch, hier im Thread zu suggerieren, TDD wuerde die Entwicklungsweise in dieser Hinsicht quasi umkehren.

Da ist eben der unterschied, unser Vor/Zurück hat keine Logik die ein Button deaktiviert oder ähnliches, ...

Genau DAS meinte ich. Wenn keine Logik, dann ist es sinnlos mit Vor- und Zurück-Buttons zu arbeiten. Mindestanforderung, um soetwas einem TabControl vorzuziehen ist eine notwendige Sequenz.

Ich würde aber vor schlagen das Thema hier sein zu lassen, da das nicht mehr zum Thema gehört.

Ich denke diese gehört eng zusammen(wieso sonst sollte es hier aufgetaucht sein?). Der Nutzen von Unit-Test hängt auch mit dem Verständnis von verschiedenen Controls zusammen. Ich muss zum Teil die Business-Logik aus diesen Controls rausziehen, um wirklichen Nutzen aus TDD oder überhaupt Unit-Tests ziehen zu können.

MfG,

N1ls

15.08.2010 - 00:10 Uhr

Wie wär es damit, gerade gefunden in mein TDD Material:

[TestMethod]  
public void Plus_FileDollarPlusFiveDollar_ReturnsSumExpression()  
{  
  Money five = Money.Dollar(5);  
  Money six = Money.Dollar(6);  
  
  IExpression result = five.Plus(six);  
  Sum sum = (Sum)result;  
  
  Assert.AreEqual(five, sum.Augend);  
  Assert.AreEqual(six, sum.Addend);  
}  

"Sum" ist eine Expression die die beiden Zahlenwerte enthält, hier wurde überprüft das "Plus" korrekt "Sum" erstellt hat.
Das in zwei Tests wäre schon recht absurd.

Das wäre schonmal ein Beispiel, wo der Testname nicht passt("Plus_FiveDollarPlusSixDollar_ReturnsSumExpression" <- mal die vertipper direkt bereinigt). Ein Test für diesen Testnamen müsste ja eigentlich folgendermassen aussehen:


public void Plus_FiveDollarPlusSixDollar_ReturnsSumExpression()
{
	Money five = Money.Dollar(5);
	Money six = Money.Dollar(6);

	IExpression result = five.Plus(six);

	Assert.IsTrue(result is Sum);
}

Das finde ich erstens einen guten Test, um sicherzustellen, dass das IExpression, was von der Plus-Methode zurückgeliefert wird, wirklich vom Typ Sum ist und zweitens prüft der Test nun genau das, was in seinem Namen steckt.

Aber wie weiter? Kommutativgesetz lassen wir mal aussen vor. Nehmen wir an, die Reihenfolge der Summanden wäre wichtig. Dann käme ich zu dem Testnamen "Plus_FiveDollarPlusSixDollar_ReturnsSumWithAugendFiveAndAddendSix". Das "And" macht mir da wieder Sorgen. Was würde es bedeuten, dies wirklich wieder auf 2 Tests aufzusplitten?


private Sum GetSumAddFiveDollarAndSixDollar()
{
	Money five = Money.Dollar(5);
	Money six = Money.Dollar(6);

	return (Sum)(five.Plus(six));
}

public void Plus_FiveDollarPlusSixDollar_SumWithAugendFive()
{
        Sum s = GetSumAddFiveDollarAndSixDollar();

        Assert.AreEqual(5, s.Augend);
}

public void Plus_FiveDollarPlusSixDollar_SumWithAddendSix()
{
        Sum s = GetSumAddFiveDollarAndSixDollar();

        Assert.AreEqual(5, s.Addend);
}

Jetzt wird eigentlich auch klar, dass die Prüfung auf ein Sum-Objekt bei der Plus-Methode gar nicht mehr notwendig ist(man könnte sie dennoch beibehalten), dies wird implizit mitgeprüft.

Ist das jetzt wirklich absurd, daraus 2 Testcases zu machen? Ich prüfe auf jeden Fall auch zwei unterschiedliche Dinge: Die Werte und deren Reihenfolge.

Dazu fällt mir etwas ein, habe ich gerade in einem Projekt gesehen.

Ich bekomme von einer Methode eine Liste von Dokumenten, die Dokumente wiederum haben eine Liste von Sprachen.
Ich muss prüfen das die Sprachen alle korrekt sind, also Gültige Sprachen.
Ich hatte zu dem Zweck einfach die Dokumente geloop und in dem loop das Assert per Linq auf die Sprach liste los gelassen.
Wie würde man so etwas korrekt Testen?

Dann bewegen wir uns schon gar nicht mehr im Bereich von Unit-Tests. Das sind Integrationstests. 😉

MfG,

N1ls

14.08.2010 - 22:41 Uhr

Hallo CSL,

ich würds wie weiter oben geschrieben halten:

Zitat von: dN!3L
Oder mit R. Osheroves Worten: "Wenn du Probleme hast, einen Test zu benennen, testet er zu viel".

Das ist sicherlich ein gutes Kriterium, alleine aber zu wenig. Gerade bei Anfängern in Sachen Unit-Tests merke ich häufig, dass diese überhaupt keine Probleme haben einen Namen für einen Test zu finden. Beispielsweise finde ich da bei einem Stack Testmethoden a la "TestPushElement". Das sieht für einen Anfänger total unproblematisch und nach einem guten Namen aus. Und innerhalb solcher Tests wird dann natürlich auf alles, was sich bewegt, mit Asserts geschossen.

Voraussetzung ist also m.E. nach erstmal, dass es Namenskonventionen für Tests gibt. Darin muss sich inhaltlich wiederspiegeln, was getan wird und was erwartet wird. Erst dann lässt sich obiges Kriterium sinnvoll anwenden.

Aber es ist irgendwie immer noch nicht ausreichend. Bleiben wir mal beim Stack:


public Push_OneElement_ElementIsTopElementOnTheStack()
{
    // Arrange
    var sut = new Stack<int>();
    
    // Act
    sut.Push(2);

    // Assert
    Assert.AreEqual(1, sut.Count);
    Assert.AreEqual(2, sut.Peek());
}

So (ähnlich) hätte ich das bis vor kurzem als Test implementiert. Ich hatte auch kein Problem damit, einen Namen für den Test zu finden. Dennoch würde ich heute sagen, es wird eindeutig zuviel getestet.
Was würde jetzt passieren, wenn im Code jedes Element versehentlich doppelt auf dem Stack landet? Der Test würde fehlschlagen mit der Meldung, dass 1 erwartet wurde, jedoch der tatsächliche Wert 2 ist. Hier wird schon deutlich, dass ich mir die optionalen Meldungen der Asserts nicht mehr sparen kann, wenn ich mehrere innerhalb eines Tests habe.
Viel schlimmer aber noch ist, dass ich hier im Testprotokoll quasi belogen werde. Wenn der Test fehlschlägt, heisst das die Erwartung "ElementIsTopElementOnTheStack" ist nicht erfüllt. Stimmt aber nicht, das oberste Element entspricht nämlich dem hinzugefügten Element.

Vielleicht ist das nicht für jeden (sofort) nachvollziehbar, aber ich fühle mich da mittlerweile mit einem extra Test "Push_OneElement_ElementCountIncreasesByOne" bedeutend besser.
Und beim Durchschauen meiner Tests geht mir das irgendwie bei jedem Test so, der mehr als ein Assert enthält. Entweder komme ich zu dem Schluss, die getesteten Dinge sind zu unterschiedlich, um in einen Test zu gehören(hier die Größe des Stacks und der Inhalt des Stacks), oder ich bin nicht damit zufrieden, wie sich das im Protokoll der Testausführung liest.

Ich bin immer noch nicht an dem Punkt, dass ich mehr als ein Assert per se verteufeln würde. Aber da müssen schon wirklich gute Gründe sprechen. Wäre schön, wenn hier jemand mal ein Beispiel hätte, wo man partout nicht ohne mehrere Asserts auskommt.

MfG,

N1ls

14.08.2010 - 11:37 Uhr

user name und passwort könnte auch mit übergeben werden, da es aber immer das selbe ist hab ich es weg gelassen.
Was meint ihr?

Ich würde es auch nicht übergeben. Jedoch würde ich wahrscheinlich die Methode eher "CreateSessionUsingValidCredentials" nennen. Das wäre eine klare Abgrenzung zu einer evtl. später notwendigen Methode "CreateSessionUsingNotExistingUsername" oder "CreateSessionUsingWrongPassword". Ob man diese braucht, hängt natürlich vom Verhalten des System under Test und den weiteren Tests ab. Man gewinnt aber zumindestens an Klarheit.

Ansonsten: Genau so finde ich die Tests sehr gut lesbar. Besser als mit mehreren Asserts. Und ich habe den Vorteil, dass jetzt alles im Testprotokoll eindeutig festgehalten wird. Man braucht den Testcode nicht mehr. Im Protokoll der ursprünglichen Version sehe ich nur sowas wie

Create_ValidSessionGenerated - Passed

jetzt dagegen:

Create_ValidCredentials_ReturnsWritableSession - Passed
Create_ValidCredentials_ReturnsLoggedInSession - Passed
Create_ValidCredentials_ReturnsMyUserName - Passed

Finde ich zumindest elegant 😉

MfG,

N1ls

14.08.2010 - 00:13 Uhr

Dh ich müsste diese Methode:
...

aufdröseln zu:

...

??

Jein.

  1. Ja, ich würde dafür mehrere Tests schreiben.

  2. Nein, ich würde (mittlerweile) keinen Test mehr schreiben, der nur überprüft, ob ich überhaupt eine Instanz zurück bekomme. Falls nicht, merke ich das sowieso. Dann schlagen mehrere andere Tests fehl und zwar mit einer NullReferenceException. Ich versuche eigentlich nur noch Verhalten zu testen. Gerade beim Test "Create_UserNameAndPassword_ReturnsSession" ist gar nicht mehr klar, ob die Session oder der SessionCreator einen Fehler hat.

  3. Nein, mein Code würde nicht so aussehen. Ich würde das Setup, was in jedem Test gleich ist, in eine Methode auslagern. Wenn sich da mal was ändert, muss man es nur an einer Stelle machen.

  4. Nein, ich würde die Methoden nicht so benennen. "Create_UserNameAndPassword_ReturnsLoggedInSession" passt nicht, besser "Create_ValidCredentials_ReturnLoggedInSession".

Ich glaube Haarspalterei kann bei Unit-Tests und insbesondere deren Benamung eine Tugend sein. Der Name eines Tests muss zwingend und möglichst genau dessen Inhalt widerspiegeln. Und der Inhalt ist mehr oder weniger eine Verhlaltensspezifikation. Und dabei ist ein "IsValid" als Erwartungshaltung immer sehr schwammig... daher lieber mehr und konkretere Tests.

Vielleicht noch einmal ergänzend: Mehrere Asserts innerhalb eines Tests finde ich nicht grundsätzlich schlimm. Wenn ich auf zu einer leeren Liste 1 Element hinzufüge, gehe ich davon aus, dass

a.) der Stack genau 1 Element umfasst
b.) genau das Element auf dem Stack liegt, dass ich hinzugefügt habe

Das in 2 Tests aufzuspalten halte ich für unnötig. Zumal der zweite Test eh fehlschlagen wird bzw. nur wenig aufschlussreich ist.

Die Frage ist dabei also, wie sehr die einzelnen Annahmen miteinander gekoppelt sind.

MfG,

N1ls

12.08.2010 - 22:17 Uhr

Damit habe ich noch nie Probleme gehabt. Sobald ein Test fehlschlägt, setze ich einen Breakpoint auf die erste Zeile meiner Asserts. Da habe ich im VS dann ja sofort das komplette Objekt auf das ich Teste vollständig unter der Lupe und kann eben die nachfolgenden Asserts am "lebendigen Objekt" auf einen Blick prüfen.

Ich sage nicht, dass das gut ist, aber es wäre halt für mich kein Argument gewesen diese Asserts auf mehrere Tests zu verteilen.

N1ls

12.08.2010 - 22:06 Uhr

Ein Vorteil wäre z.B., dass man direkt sieht, dass ein übergebenes Argument den Wert null hat und nicht innerhalb der für den Benutzer evtl. nicht einsehbaren Klasse ein Fehler ist, der zu einer NullReferenceException führt. Desweiteren kannst du natürlich einen eigenen Text für die Exception angeben und evtl. sogar mitteilen, welcher Parameter null ist usw... Natürlich mach es in diesen Fällen nur Sinn für Debugginginformationen. Vielleicht fallen jemand anderen aber auch noch andere Gründe ein?

Für mich hat das mit Debugging und evtl. Weitergabe besserer Fehlertexte weniger zu tun. Eine ArgumentNullException und eine NullReferenceException haben genau genommen eine vollständig unterschiedliche Qualität:

Auf eine ArgumentNullException kann ich in einem Grossteil der Fälle noch reagieren. Ich weiss nämlich(bzw. hoffe zu wissen), dass von der eigentlichen Aktion die ich mit dem Methodenaufruf erreichen wollte noch überhaupt nichts geschehen ist. D.h. ich bin noch am Ausgangspunkt des Aufrufs... bestenfalls konsistenter Zustand. Bei einer NullReferenceException hingegen kann ich dazu gar keine Annahmen mehr machen. Es kann sein, dass die Festplatte noch gar nicht angerührt wurde, schon halb formatiert oder zu 3/4 formatiert ist. Ich kann weder reagieren noch dem Benutzer eine entsprechende Meldung geben.

Natürlich muss man dabei immer darauf vertrauen, dass kein Unwissender mal eben eine ArgumentNullException wirft, nachdem bereits andere Anweisugen innerhalb einer Methode ausgeführt wurden...

Grüsse,

N1ls

12.08.2010 - 21:39 Uhr
public void Create_ValidSessionGenerated()  
{  
    SessionCreator creator = new SessionCreator();  
    ISession session = creator.Create("myName", "myPassword");  
  
    Assert.IsNotNull(session);  
    Assert.IsTrue(session.IsLoggedIn);  
    Assert.IsFalse(session.IsReadOnly);  
    Assert.AreEqual(session.UserName, "My User Name");  
}  

Hi,

genau solche Konstrukte hatte ich auch schon sehr häufig. Mittlerweile bin ich mir jedoch sicher, dass es sinnvoll ist dafür mehrere Tests zu schreiben.
Das Argument "wenn das erste Assert fehlschlägt, dann werden die restlichen nicht mehr evaluiert" finde ich hier als Begründung unsinnig. Dann behebe ich halt den Fehler und schaue, ob danach die folgenden Asserts korrekt sind.

Weshalb ich genau von solchen Mehrfachtests Abstand nehme ist also ein anderer Grund: Was passiert, wenn ich die Session z.B. dahingehend erweitere, dass ein aktueller Timestamp gesetzt wird? Weiterhin ist eine Session nur gültig, wenn dieser Timestamp nicht älter als x Minuten ist.
Das kann ich dann auch per Unit-Test überprüfen. Nur wer garantiert mir, dass der Implementierende nicht einen neuen Test schreibt und dabei nicht vergisst den obigen mit Erwartung "ValidSessionGenerated" auch anzupassen? Unter Umständen habe ich dann einen Test, der grün ist, aber nur weil die Validität gar nicht mehr vollständig überprüft wird.

Ich vermeide mittlerweile Testnamen, die Worte wie "valid" enthalten. Was heute noch "valid" ist, ist es morgen vielleicht schon nicht mehr. Deshalb lieber nur noch harte Fakten testen... und das geht meistens nur mit einem Assert pro Test.

Grüsse,

N1ls

10.08.2010 - 23:00 Uhr

Hallo Stefan O.,

man sollte Exceptions nicht verwenden um den Programmfluss zu steuern.

Immer dieses dogmatische Denken. Ich steuere IMMER den Programmfluss, sobald ich eine Exception werfe. Darf ich jetzt nach Deiner Aussage folgend niemals eine Exception werfen?

Den aktiven Abbruch eines lang laufenden Prozesses durch den Benutzer halte ich für einen absolut legitimen Grund, eine Exception zu benutzen. Das ist aus meiner Sicht auch kein Hack.

MfG,

N1ls

08.08.2010 - 00:50 Uhr

Begründung Nr. 1: Die Basisklasse erfüllt keine "eigene" Funktionalität, es geht nur um das Auslagern von gemeinsamem Code. Insofern ist eine Basisklasse - zumal abstract - hierfür IMHO in diesem Fall die richtige Wahl.

Begründung Nr. 2: Helper-Klassen sind mir immer suspekt. Helper-Klassen haben oft so eine Aura der Art "Ich wusste nicht, wo ich das Zeug sonst hinpacken sollte".

Hallo Golo,

das sind genau die Punkte, die aus meiner Sicht gegen eine Vererbung sprechen.

  1. Code auslagern und später wieder dranflanschen ist nicht Aufgabe der Vererbung. FCoI - Favour Composition over Inheritance. D.h. es reicht nicht aus, dass kein Grund gegen Vererbung spricht, es muss ein guter Grund dafür sprechen.
    Gerade, wenn es keine Mehrfachvererbung gibt verbaue ich mir durch soetwas diese Möglichkeit an den wirklich sinnvollen Stellen.

  2. Ja, Helper hört sich immer sehr unelegant an. Nennen wir das Kind einfach "Context" und es trifft genau das, was es ist. "In Deinem Context, benutze bitte diesen Logger"... Und das will ich gar nicht vererben. Das gebe ich per DI rein. Von daher Dein Hinweis schon ganz richtig 😉

Grüsse,

N1ls

07.08.2010 - 23:53 Uhr

Hallo zusammen,

interessante Diskussion, ich klink mich mal ein 😉

In einem Tab blätter man auch weiter, nur halt indem man auf die Tab Header klickt, nach deiner Logik müsste das umschalten der Tabs auch ins ViewModel.

Natürlich gehört das in die View, denn sie Zeigt ja die geblätterten Controls an.
Es war tatsächlich zu beginn so das die View alle Controls zuerst in Tabs anzeigte, das hatten wir dann zwecks Übersicht geändert sodass man durch die Liste iteriert.

Öhm... kurz und bündig: Nein! Kann ich überhaupt nicht unterschreiben.

Golo hat dazu schon seine Meinung geschrieben, die ich absolut nicht teile, obwohl wir zum selben Ergebnis kommen 😉

Nein, ein TabControl gehört in die View. Weil es ein Control ist, das etwas anzeigt.

Das ist meiner Meinung nach zu platt (formuliert). Wir müssen uns als Entwickler meiner Meinung nach viel mehr mit der Semantik von Controls auseinandersetzen.

Ein TabControl gehört halt nicht (nur) in die View, weil es von Control abgeleitet ist. Es gehört auch nicht in die View, weil es etwas anzeigt. Der Punkt ist, dass es nichts, aber auch gar nichts anderes tut. Natürlich kann ich durch geschicktes Anordnen der Tabs die Reihenfolge, wie sie durchgeklickt werden sollten nahelegen. Ebenso kann ich visualisieren, wie eng zwei Tabs miteinander verwandt sind, indem ich sie nahe zusammen stelle. Mehr jedoch nicht.

Was ist aber mit den "Vor"- und "Zurück"- respektive "Fertig"-Buttons? Sowas baut man nicht "zwecks Übersicht" ein. Ganz im Gegenteil. Dadurch gebe ich dem Benutzer die Hand und leite ihn. Entweder durch einen bestimmten Prozess oder aber durch ein bestimmtes Schema, welches dem besseren Verständnis dient. Unter Umständen kann ich sogar den "Weiter"-Button nicht klicken, weil eine gewissen Voraussetzung auf der aktuellen Seite nicht erfüllt ist.
Das sind alles Geschichten, die meiner Meinung nach ganz klar nicht mehr der View gehören, das ist Business-Logik.

TabControl und "Vor, Zurück"-Dialog sind also semantisch gar nicht äquivalent. Um es noch einmal zu unterstreichen, erlaube ich mir es nochmals zu zitieren:

nach deiner Logik müsste das umschalten der Tabs auch ins ViewModel.

Nein, weil hinter dem Umschalten der Tabs aufgrund der semantischen Bedeutung eines TabControls keine (Business-)Logik liegen darf.

Nur meine Meinung...

Grüsse,

N1ls

01.01.2009 - 13:03 Uhr

Hallo zusammen,

da sprichst du einen guten Punkt an, dies ist mit dem Push Prinzip in der Tat leichter zu kontrollieren.

Genau das ist aus meiner Sicht ein ganz wesentliches Merkmal, was meines Erachtens nach absolut fuer ein Pull-Prinzip spricht. Kontrolle zu haben bedeutet immer: Verantwortung übernehmen.

Nehmen wir mal das Paracetamol-Szenario und ein abgerauchtes Rechenzentrum, in dem der Update Server steht... ähm... stand. Bei einem reinen Push-Prinzip hätte demnach der Software-Anbieter die volle Verantwortung und somit die Pflicht, alle Kunden über die Änderungen wenigstens zu informieren und darauf hinzuweisen, dass diese momentan noch nicht in der Software berücksichtigt werden.
Beim Pull-Prinzip hingegen würde die Software melden "Ich konnte mich nicht zum Update-Server verbinden, Du arbeitest mit einer Version von <<DATUM>>." Dann liegt die Verantwortung jedoch ganz klar beim Kunden.

Was in diesem Fall die Gesetzgebung angeht: Ich glaube nicht, dass ein Apotheker mit der Begründung "unsere Software hat nicht gemeckert, als ich die Grosspackung Paracetamol ohne Rezept rausgegeben habe" durchkommt. Software kann in solchen Bereichen helfen, Fehler zu vermeiden, aber definitiv nicht Verantwortung abnehmen/übernehmen.

Fazit: Durch einen Pull-Mechanismus gibt der Software-Anbieter einen grossen Teil der Verantwortung an den Kunden weiter. Die Verantwortung regelmässig und zeitnah Updates zur Verfügung zu stellen bleibt, das macht dann die Produktqualität aus.

Soweit meine Gedanken dazu,
im Übrigen wünsche ich allen hier ein frohes neues Jahr 😉

N1ls

03.08.2008 - 23:32 Uhr

Hallo Bit2_Gosu,

der Thread ist zwar schon als geloest gekennzeichnet, mir fehlt hier aber auf jeden Fall noch der Begriff "Ressourcenschutzblock" 😉

Dann wird sich wahrscheinlich auch folgende Frage klaeren:

Mal schaun, ob ich finally jemals brauchen werde 😉

Mal ein ganz einfaches Beispiel, wie so ein Ressourcenschutzblock mit try und finally aussehen kann:


    FileStream fs = new FileStream("file.txt", FileAccess.Read);
    try
    {
         // do something with filestream
    }
    finally
    {
        fs.Close();
    }

Es gibt jetzt 3 Faelle, an denen deutlich wird, was dieses Konstrukt genau macht.

  1. Beim Oeffnen des Filestreams tritt eine Exception auf. Dann wird der Try-Block gar nicht erst durchlaufen und folglich auch der Finally-Block.
  2. Der Filestream kann korrekt geoeffnet werden, im Try-Block tritt keine Exception auf. Der Filestream wird dann auf jeden Fall im Finally-Block korrekt geschlossen.
  3. Der Filestream kann korrekt geoeffnet werden, im Try-Block tritt allerdings eine Exception auf. Trotzdem wird der Filestream korrekt geschlossen.

Fall 1 ist der dabei der eigentlich interessante. Daran wird deutlich, wieso der Filestream unbedingt ausserhalb des try-finally geoeffnet werden muss. Ich habe schon oefter folgendes gesehen:


    try
    {
         FileStream fs = new FileStream("file.txt", FileAccess.Read);
         // do something with filestream
    }
    finally
    {
        fs.Close();
    }

Was passiert, wenn beim oeffnen des Filestreams eine Exception ausgeloest wird? Richtig: Direkt danach wird im Finally-Block versucht einen nicht geoeffneten Filestream zu schliessen. Dann kommt es zu einer neuen Exception.

Deshalb sollte man immer Ressourcen allokieren(in diesem Falle ein File-Handle fuer den Filestream), in einem Try-Block (der nur ausgefuehrt wird, wenn die Allokierung erfolgreich war) mit diesen Ressourcen arbeiten und diese dann im Finally-Block wieder freigeben.

Kurze Anmerkung: Da FileStream das Interface IDisposable implementiert, koennte man obiges Beispiel natuerlich auch mit der Using-Direktive einfacher benutzen, aber es ging ja hier um den grundsaetzlichen Sinn von try-finally 😉

Ich hoffe durch dieses Beispiel wird die Notwendigkteit von try-finally noch ein wenig klarer. Grundsaetzlich gilt, dass die Vorraussetzungen fuer den Finally-Blocks bereits vor dem Try-Block geschaffen werden muss.

Gruesse,

N1ls

28.03.2008 - 22:41 Uhr

Hallo Golo,

Tutorial kenne ich leider keines(auch nicht in anderen Programmiersprachen), aber im Grunde genommen ist es nicht besonders schwer, eine FSM zu implementieren.

Geht es Dir eher darum, wie man diese ueberhaupt implementieren kann oder um die effizienteste Moeglichkeit? Letzteres wird man in einem Tutorial sowieso nicht finden koennen, da dies immer problemspezifisch ist und es oft einen Trade-off zwischen Speicherbedarf und Geschwindigkeit gibt.

Ich hab ehrlich gesagt auch keine Ahnung, wie die Workflow Foundation intern arbeitet. Mit dem Workflow-Editor kann man ja weitaus mehr modellieren als mit FSMs. oehm Falsch bzw. nicht ganz korrekt. Man kann eigentlich jeden beliebigen Workflow auch als FSM modellieren, nur muss man dann zusaetzliche "Hilfszustaende" einbauen. Z.B. um nebenlaeufige Prozesse abbilden zu koennen.

Meine Erfahrung: FSM implementieren ist relativ einfach. Ohne Kenntnis, was Du machen willst, ist aber schwer zu beurteilen, ob das wirklich das ist, was Du willst. Frage ist ja auch noch, ob ein Automat nach Mealy oder Moore? Die Antwort haengt immer vom Problem ab.

Vielleicht komme ich die Tage mal dazu, ein kleines Beispiel zu implementieren. Kann ich aber aus Zeitnot momentan nicht versprechen...

Gruesse,

N1ls

05.03.2008 - 20:41 Uhr

Hi Big Al,

Der Quellcode steht unter deiner
>
, die eigentlich nur kommerzielle Nutzung verbietet und ansonsten fast alles erlaubt.

Das stimmt so nicht. Auch bei nicht kommerzieller Nutzung wird immer wieder darauf hingewiesen, dass diese ausschliesslich bei akademischen Projekten etc. erlaubt ist. Von "ansonsten fast alles erlaubt" kann hier also nicht die Rede sein, eher das Gegenteil: Sehr stark begrenzt.

Finde das aber trotzdem 'ne coole Sache.

Gruesse,

N1ls

02.03.2008 - 20:40 Uhr

Hallo,

@N1ls:
Jede Tabelle, die von Anfängern benutzt wird sollte einen PK besitzen,
denn sonst können Einsteiger den Commandbuilder und/oder die Assistenten
nicht benutzen.

Das faellt dann aber eher unter die Rubrik "best practice (for beginners)". Dagegen habe ich ja gar nichts. Aber gerade in einem Einsteiger-Tutorial zu vermitteln, dass jede Tabelle zwingend einen PK haben MUSS, ist einfach falsch.

Gruesse,

N1ls

02.03.2008 - 02:11 Uhr

Sagt mir bitte eure Meinung hierzu.

Hm. Ich bin da skeptisch. Das Tutorial richtet sich an Einsteiger. Die Basics vermitteln mir, dass

  1. eine Datenbank ueber mehrere Tabellen verfuegt. Ist das so? Ich habe heute noch 'ne DB neu angelegt. Ich lasse mal die Schema-Tabellen weg. Also aus Benutzersicht und speziell aus Sicht eines Einsteigers sind da erst einmal 0 Tabellen gewesen. Und mehrere bedeutet fuer mich immer mindestens 2.

  2. jede Tabelle einen Primaerschluessel besitzt. Waere mir absolut neu. Stimmt auch Gott seid Dank nicht 😉

  3. jede Datenbank Zugangsdaten benoetigt. Stimmt auch nicht. Wer zwingt mich dazu, einer DB Zugangsdaten zu verpassen?

Das sind meiner Meinung nach ganz gravierende Fehlinformationen. Ohne ueberhaupt zu wissen, wie man eine SQL-Datenbank einrichtet, wird der Einsteiger komplett in die Irre gefuehrt.

Bitte nicht falsch verstehen. Das Projekt ein solches Tutorial zu erstellen finde ich sehr gut. Mir gefaellt auch, wie Du das darstellst. Aber dann bitte nur mit hart recherchierten Fakten und mit einem Referenzsystem zum Ausprobieren. Kein Einsteiger wird mit einem solchen Tutorial klar kommen, wenn er schon kein Testsystem hat.

Hoffe, das hilft Dir ein wenig Dein Tutorial zu erweitern / verbessern.

Gruesse,

N1ls

27.02.2008 - 23:22 Uhr

Hallo Hannes,

Wäre es eurer Meinung nach eine sinvolle Kompromisslösung, z.B. die Pakete 4+5, auf die der User kein Recht hat "disabled" anzuzeigen?
Dadurch verlagere ich diese Logik jedoch in den Client (!) - die Serveranwendung müsste alle 5 Packete zurückgeben mit der Anmerkung, dass der User eigentlich nur auf Paket 1-3 Leserecht hat.

Die Logik, was die Absicherungen der Zugangsberechtigungen angeht bleibt (bei korrekter Implementierung) weiterhin beim Server.

Etwas konkretisiert sieht es doch ungefaehr so aus, dass die 5 Pakete mehr oder weniger aus 5 Paaren von Paketnamen und Paketdaten uebertragen werden. Also:

Paketname 1, Paketdaten 1
Paketname 2, Paketdaten 2
Paketname 3, Paketdaten 3
Paketname 4, Paketdaten 4
Paketname 5, Paketdaten 5

Jetzt die Uebertragung fuer den Fall, dass nur Leseberechtigungen fuer Pakete 1-3 vorhanden sind:

Paketname 1, Paketdaten 1
Paketname 2, Paketdaten 2
Paketname 3, Paketdaten 3
Paketname 4, NULL
Paketname 5, NULL

Daten, auf die der Benutzer keine Leserechte hat, werden vom Server nicht geliefert. Wenn der Client "bemerkt", dass fuer Paket 4 und 5 keine Daten uebertragen wurden, kann er daraus schliessen, dass die Berechtigungen des Benutzers nicht ausreichen. Das kann dann durch eine entsprechende Darstellung kenntlich gemacht werden (z.B. auch disabled) oder sogar ausgeblendet werden. Kann natuerlich auch noch durch ein zusaetzliches Flag realisiert werden, aber sicherheitstechnisch liegt die volle Kompetenz "wer darf was lesen?" beim Server.

Gruesse,

N1ls

25.02.2008 - 20:40 Uhr

Unterstützt jeder FTP Server im passive-mode auch den active-mode, sodass ich einfach den Code so ändern kann, dass ich darüber zugreifen könnte oder muss ich dafür auch auf dem Server etwas umstellen?

Es geht ja anscheinend um Deinen Win-Server... also einfach ausprobieren 😉

24.02.2008 - 23:36 Uhr

Ich verbinde über den Port 21 und habe jetzt bereits mehrmals gelesen, dass zum Datentransfer Port 20 benutzt wird.

Das ist so ungefaehr die halbe Wahrheit. Bei FTP-Uebertragung werden beim Dateitransfer immer auch irgendwelche unprivileged Ports (> 1023) benutzt. Im Active-Mode stellt der Client diesen Port zur Verfuegung, was problematisch ist, wenn man hinter einem NAT-Router o.Ae. sitzt, im Passive-Mode wird dieser Port vom Server bereitgestellt (dementsprechend evtl. Firewall-Probleme).

Der Server ist ein Windows Server 2003, auf dem sowohl Port 20 als auch Port 21 in der Firewall durchgeschaltet wird.

Wie erwaehnt, reicht das nicht, wenn versucht wird ueber den Passive-Mode zuzugreifen.

Der Datentransfer mittels "normaler" FTP-Clients wie dem WindowsCommander oder WSFTP_LE funktioniert übrigens prächtig.

In der Regel versuchen die FTP-Clients auf verschiedene Weise zuzugreifen. Also, wenn Passive nicht funzt, dann wird Active versucht. Probier mal mit dem Internet Explorer auf den ftp zu kommen ("ftp://servername/verzeichnis" bzw. mit Authentifizierung "ftp://username:passwort@servername/verzeichnis"). Das wird, denke ich mal, auch nicht funktionieren.

Kann ich die Datei eigentlich auf einem anderen Wege als per FTP übertragen?

Klar. Z.B. WebDAV, Http-Upload, eigener TCP-Server auf dem Zielrechner, Remoting, einfache Windows-Freigabe. Die Moeglichkeiten sind eigentlich unbegrenzt. Einzige Voraussetzung: Ausreichende Berechtigungen auf dem Server 😉

Hope that help, Gruesse,

N1ls

28.01.2008 - 01:45 Uhr

Hallo tire0011,

Problematik: ich habe zwei Arrays und möchte den Unterschied einem dritten Array zuweisen. Die Suche soll auf Leistung ausgerichtet sein, also sehr schnell gehen. Unten ist ein Beispiel-Code, in Zukunft könnten die Arrays umfangreicher sein, daher die Frage.

...
folgenden Beispiel Code:

  
static void Main(string[] args)  
        {  
            string[] Array1 = {"cycle", "rectangle", "line"};  
            string[] Array2 = {"cycle", "rectangle", "line", "connector","stencil"};  
           .  
           .  
           .  
        }  
  

Im Array3 ist, soll folgendes Ergebnis enthalten sein:
Array3[0] = "connector";
Array3[1] = "stencil";

Nochmal eine Nachfrage zum Problem: Meiner Meinung nach deckt sich Deine Problembeschreibung nicht mit dem von Dir geposteten Algorithmus. Woran kann man sich eher orientieren, an der Beschreibung oder dem Algorithmus?

Ich fasse mal Deinen Algorithmus in Worte: Array3 beinhaltet alle Elemente, die in Array2 zusaetzlich zu den Elementen in Array1 vorhanden sind.

Ist es das, was Du willst? Koennen z.B. folgende Faelle auftreten und was erwartest Du als Ergebnis in Array3?

Fall1:

string[] Array1 = {"cycle", "rectangle", "line", "connector","stencil"};
string[] Array2 = {"cycle", "rectangle", "line"};

Fall2:

string[] Array1 = {"xyz", "rectangle", "line", "connector","stencil"};
string[] Array2 = {"cycle", "rectangle", "line"};

Falls diese Faelle auch beruecksichtigt werdem sollen, dann erhoeht sich die Komplexitaet des Problems weiter. Die Loesung Dictionary/Hash waere sicherlich immer noch die Loesung, aber der Algorithmus muesste auch entsprechend angepasst werden.

Gruesse,

N1ls

27.01.2008 - 23:49 Uhr

Hallo Herbivore,

an dem Punkt stimmen wir ja über ein. Das habe ich ja schon oben geschrieben. Der Sortieraufwand entfällt bei Dictionary..

Oops. Sorry, Deinen Satz ueber das Sortieren hab ich ueberlesen und daher angenommen, dass Du es als "Aepfel mit Birnen"-Vergleich gesehen haettest.

Okay, hab's jetzt nochmal genau gelesen und es ist immer noch kein A&B-Vergleich. Einfach aus dem Grunde, dass ich nichts verglichen habe, sondern nur festgestellt habe, dass die Add-Operation lediglich den Aufwand O(1) hat. Das ist ja nicht selbstverstaendlich 😉

Um jetzt aber nicht zu sehr an solchen Feinheiten neben dem Topic herzuschwameln

@zommi

5000 Durchlaeufe, um Messungenauigkeiten zu umgehen ist ein gutes Mittel. Viel interessanter waere aber mal, wie Du die Testdaten generiert hast. Weil interessant waere hier ja mal eine Gegenueberstellung der verschiedenen Ansaetze mit unterschiedlichen Testdatengroessen.

Wobei... hm... GUIDs?

Gruesse,

N1ls

27.01.2008 - 18:47 Uhr

Hallo herbivore,

Allerdings vergleichst du hier Äpfel mit Birnen. Das Hinzufügen (Add) eines Elements zu einer Liste kostet mit O(1) genau soviel wie das Hinzufügen zu einem Dictionary. Und wenn man eine ganze Dictionary kopiert, kostet das genauso O(n) wie das Kopieren eine Liste O(n) kostet. Hier liegen die Performance-Vorteile also nicht.

Vergiss dabei aber nicht, dass die Binaersuche nur mit einer sortierten Liste funktioniert. D.h. ich muss also das Array sortieren, sonst funktioniert der Algorithmus nicht (hat tire0011 auch korrekterweise in dem Code-Beispiel gemacht).

Von daher profitiere ich an dieser Stelle zusaetzlich vom Hash... ich spare mir das Sortieren, muss aber im Gegenzug einmal das Array in das Dictionary kopieren. Und dabei ist der Aufwand O(n) in der Regel guenstiger als jeder Sortieralgorithmus.

Gruesse,

N1ls

27.01.2008 - 18:14 Uhr

Ja, Dictionary<>.ContainsKey. Der Aufwand ist nur O(1).

Vielleicht waere hier noch anzumerken, dass der Aufwand fuer das Einfuegen im Dictionary ebenfalls O(1) ist, dementsprechend das Kopieren der Elemente aus dem Array O(n).

Da duerfte man also zusaetzlich nochmal Performance gegenueber dem Sortieren des Arrays rausholen 🙂

Gruesse,

N1ls

10.01.2008 - 19:35 Uhr

Hallo Gregor, hallo N1ls,

Class A darf ohne Class B nicht existieren
Wikipedia sagt zur Komposition:

Ein Teil kann immer nur genau einem Ganzen zugeordnet sein. Konkret lassen sich daraus die Multiplizitäten ableiten. Teile, die über eine Komposition mit einem Ganzen verbunden sind, dürfen jeweils in höchstens einem (0..1) Ganzen vorkommen.
Der erste Satz unterstütz eure Sicht. Der letzte Satz heißt aber, dass A sehr wohl ohne B existieren darf. A darf nur nicht in mehr als einem B enthalten sein.

Hallo herbivore,

Hier mal eine weitere Quelle zu dem Thema:

[quote=Christoph Kecher, UML 2.0 - Das umfassende Handbuch (2. Auflage 2006)
Wird das Ganze zerstoert, endet auch die Existenz der Teile. Auf der anderen Seite kann das Ganze in der definierten Form nicht weiterexistieren, wenn ihm auch nur eines der Teile wieder entnommen wird. Um dies zu verhindern, ist das Ganze fuer die Erstellung und Beseitigung der Teile verantwortlich

Aufgrund der starken Verbindung kann ein Teil gleichzeitig nur in in einem Ganzen existieren, weshalb die Multiplizitaet auf Seite des Ganzen immer 1 ist und daher auch weggelassen werden kann

Ich finde die Formulierung ein wenig klarer als bei Wikipedia. Meiner Meinung nach bedeutet es wirklich, dass ein Teil auch autark existieren kann.

Interessant aber auch der erste Absatz. Demnach machen bei einem Kompositum beim Ganzen setter-Methoden fuer die Teile keinen Sinn. Oder sehe ich das falsch?

Gruesse,

N1ls

07.01.2008 - 23:34 Uhr

Eine möglichkeit wäre vielleicht wirklich das sperren des Profi Bereiches und eine freischaltung ab z.B. 200 Beiträgen oder durch einen Admin der drauf angesprochen wurde bzw. merkt das sich ein User auf einem gewissen level befindet. Aber das wäre wiederrum für einige nur schwer zu verstehen und hätte einen hohen Administionsaufwand.

Ohne mir da jetzt eine irgendwelche Kompetenzen anmassen zu wollen: Sperren ist aus meiner Sicht absolut indiskutabel. Eine Sperre kann ja eigentlich auf genau 2 Arten erfolgen:

  1. Visibility-Sperre
    Der Profi-Bereich ist nur fuer Benutzer > 200 Beitraege (siehe oben) sichtbar. Geht eigentlich gar nicht. Ein Forum ist niemals nur eine Diskussionsplattform, sondern auch eine riesige Knowledge-Base. D.h. die Sichtbarkeit einzuschraenken wuerde verhindern neue User zu gewinnen. Auch Anfaenger sind bestrebt sich Foren zu suchen, in denen viel Knowledge vorhanden ist.

  2. Readonly-Sperre
    Im Profi-Bereich duerfen nur Profis posten. Hier wird wieder verhindert, Knowledge zu erweitern. Zwar kann jedermann durch Suchmaschinen hilfreiche Anregungen zu Problemen finden, da diese fuer jedermann lesbar sind, aber sobald aufgrund dieser Anregungen eigene Erkenntnisse hinzugefuegt werden wollen steht man vor einer Wand. Ich melde mich vielleicht in einem Forum welches ich durch Google und einem spezifischen Problem gefunden habe an, um meine Erkenntnisse zu schreiben, aber ich werde nicht noch 200 Postings verfassen, bis ich dieses dann endlich darf.

Vermutlich würde nur ein Bruchteil der Anfänger seine Posts dorthin platzieren, aber falls man eine ausreichend große Gruppe von Leuten hätte (Poweruser?), die Postings verschieben könnte, dann wäre es durchaus möglich.

Auch das kann eigentlich keine Loesung sein. Newbie meldet sich an, eroeffnet ein Thema. Das wird in den Bereich "Anfaengerfragen" verschoben. Newbie macht neuen Thread auf "Wo ist mein Thread abgeblieben?" 😉
Abhilfe schaffen hier natuerlich die "Verschoben"-Verweise, aber die tragen in der Regel auch nicht unbedingt zur Uebersichtlichkeit eines Forums bei.

Wie schon mehrfach hier zu lesen, wird es eine wirkliche Koenigsloesung kaum geben. Evtl. die Threads eindeutig beratungsresistenter Kandidaten frueher schliessen und fuer die anderen innerhalb der Community einen Leitfaden zu entwickeln, wie Probleme vernuenftig formuliert werden koennen... Nicht jeder Threadtitel "Ich hab da ein Problem" entspringt purer Bosheit. Manchmal wissen die Leute einfach nicht, wie sie ihre Probleme besser formulieren koennen/sollen.

My 2 Cents,

Gruesse,

N1ls

01.01.2008 - 23:06 Uhr

Oh, wow! Ich glaube, ich hab's jetzt! Ein Freund hat mich auf die Formel

  
      double TanWinkel = (PosY - ZielY) / (PosX - ZielX);  
      double Winkel = Math.Atan(TanWinkel);  
  

aufmerksam gemacht. Und siehe da: Wenn ich das Ergebnis als Bogenmaß interpretiere, bekomme ich einen Winkel von 45 Grad raus!

Ahh 🙂 Da ist das von mir vermisste Minus-Zeichen 🙂

Du bist definitiv auf dem richtigen Weg, aber Vorsicht! Sobald beide Objekte die gleiche X-Koordinate haben, laeufst Du in eine Exception. Den Sonderfall musst Du behandeln.

01.01.2008 - 16:04 Uhr
  
    // Class A darf ohne Class B nicht existieren .. also man könnte an sich den GC auch noch starten  
  

Ich haette beinahe widersprochen, muss aber zugeben, dass sich die Aussage mit meinen Recherchequellen deckt.

Nach neuem Wissensstand muss ich dann aber hinzufuegen:

Class A darf nur von Class B instanziiert werden koennen (ansonsten waere die Voraussetzung Class A kann nicht ohne Class B existieren verletzt).

Auf jeden Fall Danke Gregor, hat mir auch wieder etwas mehr Klarheit verschafft.

Gruesse,

N1ls

01.01.2008 - 15:42 Uhr

Mein Ansatz wäre:

cos a = (X1 * X2 + Y1 * Y2) / (sqrt(X1² + Y1²) * sqrt(X2² + Y2²))
...

Ich hab keine Ahnung, wie Du auf diese Formel kommst und ob diese annaehrend korrekt sein kann. Fuer mich persoenlich muss grundsaetzlich ein Minus-Zeichen in einer Formel auftauchen, wenn aus Koordinatenpaaren ein Winkel berechnet werden soll.

Gruesse,

N1ls

01.01.2008 - 15:26 Uhr

Hallo ,
ich wollte fragen

wie sieht eine "Aggregation" (UML) in einen CSharp Programm?
ist es wie Folgend ?
Wenn ich sage dass die Klasse "A" enthält eine Klasse" B"

Nein, das geht sogar eher in Richtung Komposition und selbst das trifft es noch nicht.

Aggregation sieht eher so aus:


public class Produkt
{
}

public class Inventar
{
   Produkt[] mProdukte;
}

Das ist eine ziemlich klassische 1:n Aggregat-Beziehung. Ein Inventar-Objekt hat eine Beziehung zu n Produkt-Objekten.

Kurzer Exkurs zum Unterschied Aggregat und Komposition: Bei der Komposition ist die Beziehung unter den Objekten viel staerker bindend. Beispielsweise Auto und Motor. Ein Auto-Objekt kann zwar ohne ein Motor-Objekt existieren, es kann aber nicht fahren. Ein Aggregat stellt hingegen lediglich eine lose Beziehung zwischen Objekten dar. Ein bestimmtes Produkt(-Objekt) ist im Inventar(-Objekt) enthalten, jedoch nicht fuer dessen funktionsfaehigkeit notwendig.

Ich hoffe, das war einigermassen verstaendlich erklaert und hilft weiter.

Gruesse,

N1ls

01.01.2008 - 14:27 Uhr

empfiehlt die 3.1 mir glatt, ich soll die (float) wegnehmen, die wären unnötig.

Ein float ist auch unnoetig. Reicht vollkommen aus, wenn einer der beiden Operanden explizit zu float gecastet wird. 😉

01.01.2008 - 13:07 Uhr

Das serialisieren dauert laut Teststopuhr 3,6 sekunden.

Zum Deserialisirern brauch ich dann 6,5 Sekunden ca.

Das reine lesen und Speichern der Daten in der Liste, aso ohne serialisieren dauert gerade mal 0.9 Sekunden.

Ist das normal ?

Dass das Deserialisieren doppelt so lange dauert, wie das Serialisieren ist vollkommen normal. Aus folgenden beiden Gruenden:

  1. Speicherreservierung
    Beim Serialisieren sind alle Objekte bereits vorhanden, es muss kein Speicher mehr reserviert werden. Beim Deserialisieren muss fuer jedes Objekt erstmal neuer Speicher reseviert und das Objekt neu angelegt werden.

  2. Splitting / Bekanntheit der Daten
    Waehrend Serialisierung der Serialisierung sind die zu schreibenden Daten bereits bekannt und muessen nur in den Stream "gepumpt werden". Die Deserialisierung ist weitaus komplexer, das dort die eingehenden Strings zerlegt werden und dafuer einige Stringvergleiche bzw. Stringoperationen notwendig sind. Stringoperationen sind grundsaetzlich ziemlich teuer was Rechenzeit angeht.

Kann man das irgendwie beschleunigen ?

Falls die Daten nicht noch von anderer Stelle in genau der Form verarbeitet werden muessen, kann man da meiner Meinung nach noch etwas rausholen, wenn man die Daten binaer anstatt in Textzeilen speichert.

Gruesse,

N1ls

29.12.2007 - 13:56 Uhr

Hauptsaechlich VS 2005 Prof., daneben noch zu Testzwecken VS 2008 Express. Mein Plan geht jedoch dahin in Zukunft komplett auf 2008 zu wechseln, da die 2005er Version teilweise noch mit Schwierigkeiten unter Vista behaftet ist.

29.12.2007 - 13:46 Uhr

Problem gelöst..!

Der letzte Quellcode funktioniert jetzt..!

Schoen waere jetzt natuerlich zu erfahren, WIE das Problem geloest wurde bzw. woran es letztendlich gelegen hat. Einfach um fuer zukuenftige Probleme dieser Art Anhaltspunkte zu haben.

Noch mal was anderes zu dem Thema, wenn ich jetzt das Dreieck was grün ist, nachträglich irgendwann in meinem Programm gelb habe möchte, wie mache ich das am besten ?

Wie kann ich am saubersten programmieren `?

Einfach eine Property fuer die Farbe definieren.


Pen farbeZustandX = new Pen(Color.Green, 3);

public Color farbeX
{
      get 
      {
            return farbeZustandX.Color;
       }
       set 
       {
            farbeZustandX = new Pen(value, 3);
       }
 }

Intern arbeitest Du mit dem Pen-Objekt, bei der Property interessiert allerdings nur die Farbe, also ein Color-Objekt.
Das ist IMHO die sauberste Loesung, da fuer dem Benutzer der Klasse gar nicht beachtet werden muss, ob intern ein Pen (fuer DrawRectangle) oder Brush(fuer FillRectangle) verwendet wird. Es interessiert nur die Farbe. Man koennte natuerlich auch die Farbe in einem Color-Objekt speichern, muesste dann aber in jedem Paint-Event ein neues Pen- bzw. Brush-Objekt erzeugen, was fuer gewaltigen Overhead sorgt.

29.12.2007 - 13:01 Uhr

Das würde ja eh nur gehen wenn die Software aufn Switch läuft, da nur der Switch den Netzwerktraffic von allen 3 PCs sieht.

Das ist so nicht ganz richtig. Die Software muss nicht zwangsweise auf dem Switch laufen, wenn dieser SNMP unterstuetzt. MRTG z.B. ist ein Tool, was genau darauf aufsetzt.

29.12.2007 - 12:56 Uhr

Und da funktioniert das System....
Der einzigste Unterschied, ist das die Properties der Form anders sind!

Das ist doch schon mal ein Fortschritt 🙂

Unglaublich........
Woran mag das denn wohl liegen?? Ne Idee ?

Dazu muss man ja eigentlich nur mal untersuchen, welche Properties unterschiedlich gesetzt sind. Um das zu sehen, waere mal interessant den Code aus "InitializeComponent" zu sehen.

29.12.2007 - 12:19 Uhr

Achsooo.....
Diese Funktion ist bei mir deaktiv...ich nutze ein Vollbild ohne "maximizebox" !

Was nun, wenn der Paint Event nicht ausgelöst wird bei mir, bei Dir aber schon !?

Noch ne Idee..?

Das einfachste waere wahrscheinlich, Du machst mal ein Mini-Beispiel und haengst das einfach komplett als Zip-Datei hier an.

Rein vom Quellcode sehe ich absolut keinen Grund, warum der Event-Handler nicht aufgerufen wird.

29.12.2007 - 12:03 Uhr

Fenster mal maximiert

Wie soll ich das deuten? Was soll ich den maximieren ?
*** BIN NOCH NEULING ****

C#-Neuling oder Windows-Neuling? 🤔

Die meisten Windows-Fenster haben oben rechts einen Button zum Schliessen und daneben auch einen zum Maximieren. Und wenn man darauf klickt (ole ole) maximiert sich das Fenster auf die volle Monitorgroesse. 8)

29.12.2007 - 11:51 Uhr

Hi !
Ich bekomme es immer noch nicht auf die Kette !
Das Problem muss woanders liegen...

...
Das andere Programm habe ich auch schon ausprobiert, nix passiert.

Beim Debuggen habe ich das Problem das er die
public void Form1_Paint(object sender, PaintEventArgs e) { ... } nicht aufruft !

Was ist da los ???

Obiger Code funktioniert bei mir einwandfrei und auch beim Debuggen springt er in Form1_Paint rein. Hast Du das Fenster mal maximiert (einerseits um sicherzugehen, dass im sichtbaren Bereich gezeichnet wird, andererseits um definitiv ein Paint-Event auszuloesen) ?