Archiv

Artikel Tagged ‘Microsoft .NET’

Probleme bei der Verwendung von System.Double

30. August 2010 Mario Noack Keine Kommentare

Bezüglich der Verarbeitung von Komma-Werten habe ich heute wieder einge Erkenntnisse gewonnen, die teilweise auch aus der verbesserten Dokumentation vom Microsoft.NET Framework 4 her rührten. Dort ist unter anderem zum Typ Double folgendes zu lesen:

Bei Verwendung einer Gleitkommazahl könnte ein Wert möglicherweise nicht wiederhergestellt werden.

Das Problem verdeutlicht folgendes gekürztes Beispiel aus der MSDN:

double fromLiteral = -4.42330604244772E-305;
double fromParse = Double.Parse("-4.42330604244772E-305");
 
Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral);
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse);
 
// On 32-bit versions of the .NET Framework, the output is:
//    Double value from literal:        -4.42330604244772E-305
//    Double value from Parse method:   -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
//    Double value from literal:      -4.4233060424477198E-305
//    Double value from Parse method:   -4.42330604244772E-305

Das die hinteren Stellen einer Double-Zahl Ungenauigkeiten unterliegen, war mir schon länger klar. Das der selbe Code auf verschiedenen Systemen aber andere Genauigkeiten erzielen kann, war eine neue Erkenntnis, die bis dato nur als ungeäußerte und nicht beweisbare Vermutung im Raum stand.

Um das Problem zu umgehen, habe ich zwei Extensions für mich geschrieben:

public static bool NearZero(this double value)
{
    return Math.Abs(value) < 1e-12;
}
public static bool NearValue(this double value, double compareValue)
{
    if (value.NearZero() && compareValue.NearZero()) return true;
 
    return Math.Abs(value - compareValue) < 
        Math.Min(Math.Abs(value), Math.Abs(compareValue)) * 1e-12;
}

Mit der ersten prüfe ich auf 12 Stellen hinter dem Komma, ob die Zahl fast Null ist. Mit der zweiten Methode kann ich simple 2 Werte auf annähernde Gleichheit überprüfen.

.NET Open Space Süd 2010

Dieses Wochenende fand in Karlsruhe der .NET Open Space Süd 2010 statt. Für mich war es das erste Mal, das ich an so einer Veranstaltung teilgenommen habe. Die Veranstaltung wurde in den Räumen von bluehands ausgerichtet.
Die Themen wurden durch die ca. 50 Teilnehmer selbst bestimmt. Dabei legte sich automatisch ein großer Fokus auf die Schnittstelle zwischen Kunden und Entwickler. Hier stecken wohl für alle die größten Probleme bzw. es ist einfach der meiste Handlungsbedarf. Es gab einige Sessions in Richtung BDD und DDD. Diese Sessions waren sehr informativ und es fand eine angeregte Diskussion ab. Ein Fazit der Veranstaltung war für viele, dass wir Entwickler mit Tools für die Entwicklung selbst, gut versorgt sind. Wo es mangelt, ist eine sinnvolle Unterstützung durch Software, Anforderungen des Kunden zu erfassen und umzusetzen bzw. deren Umsetzung zu prüfen.
Ein Highlight der Veranstaltung war ein kleines DoJo, initiert durch Ilker. Thema war die Umsetzung von arabischen Zahlen in das römische Zahlensystem.

Beispiele für String-Formatierungen

6. August 2009 Mario Noack Keine Kommentare

Ein Beispiel sagt oft mehr, als tausend Worte… Um mit .NET Strings zu formatieren, gibt es viele Möglichkeiten, leider ist die Hilfe dazu zwar oft korrekt, aber nicht wirklich erklärend.

Darum hier ein Link mit praktischen Beispielen dazu sind hier zufinden:

http://alexonasp.net/samples/stringformatting/

Sogar die Ländereinstellungen sind umschaltbar.

Master Detail mit List<>

Möchte man Listen (System.Collection.Generic.List<>) an Comboboxen mittels einer Master-Detail-Beziehung anbinden, so bietet sich die hier dargestellte Vorgehensweise an.

Code der Listen:

    public class Detail
    {
        public String Caption { get; set; }
        public int Id { get; set; }
 
        public Detail(string caption)
        {
            Caption = caption;
        }
    }
 
    public class DetailList: List<Detail>
    {
        public String DetailListCaption { get; set; }
 
        public DetailList(string masterCaption)
        {
            DetailListCaption = masterCaption;
        }
    }
 
    public class Master: List<DetailList>
    { }

Zur Anwendung wird die Liste bespielhaft befüllt:

    private Master master = new Master();
...
    master.Add(new DetailList("Cap Master 1"));
    master.Add(new DetailList("Cap Master 2"));
 
    master[0].Add(new Detail("Cap Detail 1.1"));
    master[0].Add(new Detail("Cap Detail 1.2"));
    master[1].Add(new Detail("Cap Detail 2.1"));
    master[1].Add(new Detail("Cap Detail 2.2"));

Nun werden noch zwei BindingSource-Komponenten erzeugt und initialisiert:

    masterBindingSource = new BindingSource {DataSource = typeof (Master)};
    comboBox1.DataSource = masterBindingSource;
    comboBox1.DisplayMember = "DetailListCaption";
 
    detailBindingSource = new BindingSource {DataSource = typeof (DetailList)};
    comboBox2.DataSource = detailBindingSource;
    comboBox2.DisplayMember = "Caption";
 
    masterBindingSource.CurrentChanged += masterBindingSource_CurrentChanged;
...
    private void masterBindingSource_CurrentChanged(object sender, EventArgs e)
    {
        detailBindingSource.DataSource = masterBindingSource.Current;
    }

Wichtig ist dann nur noch die Initialisierung der Datasource mit einem konkreten Wert:

    masterBindingSource.DataSource = master;
KategorienMicrosoft .NET Tags: ,

Runden in Microsoft .NET

Es gibt in Microsoft .NET alle gebräuchlichen Möglichkeiten, Zahlen zu runden:

using System;
 
namespace RoundTest
{
    class Program
    {
        static void TestOutput(double value)
        {
            Console.WriteLine();
            Console.Write(value);
            Console.Write("\t Truncate: " + Convert.ToInt32(Math.Truncate(value)));
            Console.Write("\t Ceiling : " + Convert.ToInt32(Math.Ceiling(value)));
            Console.Write("\t RoundAFZ: " + Convert.ToInt32(Math.Round(value, MidpointRounding.AwayFromZero)));
            Console.Write("\t RoundToE: " + Convert.ToInt32(Math.Round(value, MidpointRounding.ToEven)));
        }
 
        static void Main(string[] args)
        {
            TestOutput(10.0);
            // ...
            TestOutput(-10.51);
            Console.ReadLine();
        }
    }
}

Hier ist die Ergebnisausgabe davon zu finden:

10       Truncate: 10    Ceiling : 10    RoundAFZ: 10    RoundToE: 10
10,49    Truncate: 10    Ceiling : 11    RoundAFZ: 10    RoundToE: 10
10,5     Truncate: 10    Ceiling : 11    RoundAFZ: 11    RoundToE: 10
10,51    Truncate: 10    Ceiling : 11    RoundAFZ: 11    RoundToE: 11
 
-10      Truncate: -10   Ceiling : -10   RoundAFZ: -10   RoundToE: -10
-10,49   Truncate: -10   Ceiling : -10   RoundAFZ: -10   RoundToE: -10
-10,5    Truncate: -10   Ceiling : -10   RoundAFZ: -11   RoundToE: -10
-10,51   Truncate: -10   Ceiling : -10   RoundAFZ: -11   RoundToE: -11

kick it on dotnet-kicks.de

KategorienMicrosoft .NET Tags: ,

C# Extensions in Microsoft .NET 2

19. Februar 2009 Mario Noack Keine Kommentare

Möchte man Extensions in C# verwenden, scheint man auf das 3er Net-Framework angewiesen zu sein. Dies ist aber nicht ganz richtig. Legt man folgende Klasse in dem Projekt an:

namespace System.Runtime.CompilerServices
{
    public class ExtensionAttribute : Attribute {}
}

so kann man Extensions, eine 3er Compiler vorrausgesetzt (VS2008), problemlos mit dem .NET Framework 2 als Ziel verwenden.

KategorienMicrosoft .NET Tags: ,

neue C# Anweisung yield return

7. Februar 2009 Mario Noack Keine Kommentare

Bereits mit der Version 2.0 wurde C# um ein mächtiges Feature erweitert: yield return.
Mit C# 3.0 kam dann unter anderem das var dazu. Dazu ein kleines Beispiel:

private static void TestList()
{
    var list = new List<String> { 
        "Dies ist Zeile 1", 
        "HIER KOMMT ZEILE 2", 
        "3 hat nichts", 
        "4 IST NOCH MAL EIN TREFFER" };
    foreach (var item in list)
    {
        Console.WriteLine(item);
    }
 
    Console.WriteLine();
 
    foreach (var item in FilterUpperLines(list))
    {
        Console.WriteLine(item);
    }
}
 
private static IEnumerable<String> FilterUpperLines(List<String> list)
{
    foreach (string elem in list)
    {
        if (elem.ToUpper() == elem)
            yield return elem;
    }
}

var kann bei einer Variablendeklaration anstelle des Typs gesetzt werden, sofern die Definition für den Compiler zwingend ist. Das macht meiner Meinung nach immer dann Sinn, wenn der Typ beispielsweise durch die Initialisierung direkt dahinter nochmals wiederholt wird.

yield kann einem sehr viel Arbeit abnehmen, wenn man Mengen zurückgeben möchte. yield return sammelt quasi die Ergebnismenge ein und gibt sie in Form einer Liste zurück. Das eigentliche Definieren und zurückgeben der Liste übernimmt komplett der Compiler.

Der Clou am var ist übrigens: Diese Funktionen lassen sich auch bei einem Compilat für das Net-Framework 2.0 nutzen! Der Compiler braucht für die Funktionalität keine Features aus dem 3er Framework.

KategorienMicrosoft .NET Tags: ,

Set’s in C#

2. Dezember 2008 Mario Noack Keine Kommentare

Seit dem Wechsel von Delphi auf C# habe die sprechende Art, mit Aufzählungen zu arbeiten, vermisst:

type
  Right = (rRead, rWrite, rDelete);
  Rights = set of Right;
 
var rightSet: Rights;
 
begin
  rightSet := [rRead, rWrite];
  Writeln('Schreibrecht: ' + BoolToStr(rWrite in rightSet));
  rightSet := rightSet - [rWrite];
  Writeln('Schreibrecht: ' + BoolToStr(rWrite in rightSet));
  Readln;
end.

Das gleiche in C# war durchaus effektiv und klar, aber die boolschen Operationen lassen sich oft nicht so intuitiv lesen bzw. umsetzen:

[Flags]
enum Rights
{
    Read = 1,
    Write = 2,
    Delete = 4,
}
 
private static void BoolscherCode()
{
    // üblicher Code
    Rights rights = Rights.Read | Rights.Write;
 
    // prüfen, ob Schreiben enthalten ist:
    Console.WriteLine("Schreibrecht: " + ((rights & Rights.Write) == Rights.Write).ToString());
 
    // Schreibrecht entfernen
    rights &= ~Rights.Write;
 
    // prüfen, ob Schreibrecht enthalten ist:
    Console.WriteLine("Schreibrecht: " + ((rights & Rights.Write) == Rights.Write).ToString());
}

Wintellect bietet mit seiner PowerCollection hier durchaus einen Ausweg:

enum Rights { Read, Write, Delete }
 
private static void ListenCode()
{
    Wintellect.PowerCollections.Set rights = new Wintellect.PowerCollections.Set();
    rights.Add(Rights.Read);
    rights.Add(Rights.Write);
 
    // prüfen, ob Schreiben enthalten ist:
    Console.WriteLine("Schreibrecht: " + (rights.Contains(Rights.Write)).ToString());
 
    // Schreibrecht entfernen
    rights.Remove(Rights.Write);
 
    // prüfen, ob Schreibrecht enthalten ist:
    Console.WriteLine("Schreibrecht: " + (rights.Contains(Rights.Write)).ToString());
}
KategorienMicrosoft .NET Tags: ,