Rainer's profileCyrons BlogPhotosBlogListsMore Tools Help

Blog


    March 29

    Wikipedia bekommt 3 Mio $ Spende von der Alfred P. Sloan Stiftung

    Diese Schlagzeile ist zur Zeit ziemlich allgegenwärtig im Internet.
    Meine Meinung dazu: Das schmeckt mir nicht. Warum, das möchte ich kurz erläutern. Wer geneigt ist, den nehme ich noch einmal mit auf meine Recherche-Tour durch das Web. Zuerst einmal: Wer ist oder war Alfred P. Sloan und welche Ziele verfolgt die Stiftung?

    Alfred P. Sloan gründete diese Stiftung im Jahre 1934. Mehr dazu hier

    http://de.wikipedia.org/wiki/Alfred_P._Sloan

    besser noch ist dieser Link:

    http://en.wikipedia.org/wiki/Alfred_P._Sloan_Foundation

    Die Eckpunkte sind also: Alfred P. Sloan war der CEO von General Motors. Er schuf einige Innovationen auf dem unternehmerischen Sektor. Um es erst einmal ganz grob zu definieren, er war ein auf Profitmaximierung ausgerichteter Mensch.

    Nun zur Stiftung, da gibt der englische Artikel bessere Auskunft. Sie unterstützt Forschung, Entwicklung, Bildung und Karrieren in den Bereichen Wissenschaft, Technologie, Lebensstandards (?) und ökonomische Performance.

    Lebensstandards und ökonomische Performance? Klingt das nur für mich nach, "Lasst uns sehen, wie weit wir die Gesellschaft ausbeuten können, bevor sie kollabiert" ?

    Auf mich macht das jedenfalls keinen guten Eindruck. Was passiert jetzt, wo Wikipedia diese Spende bekommt? Ich fürchte, damit hat sich die größte Wissensbibliothek seit Alexandria in die Abhängigkeit manövriert. Wird Wikipedia, seiner Freiheit beraubt, noch genau so glaubwürdig sein? Wird es weiterhin ein objektiver Raum ohne Werbung sein?

    Kommentare sind erbeten.

    March 24

    foreach, IEnumerable und IEnumerator

    Was hat es mit der foreach-Schleife auf sich? Diese Frage möchte ich hier beantworten. Dazu erst ein Beispielcode von MSDN (Copyright by Microsoft).
    Was im einzelnen passiert, erläutere ich im Anschluß.

    using System;
    using System.Collections;
    
    namespace IEnumeratorTest
    {
       class Book
       {
          internal Book(string name, decimal price)
          {
             Name = name;
             Price = price;
          }
    
          internal string Name { get; private set; }
          internal decimal Price { get; private set; }
       }
    
       class Library : IEnumerable
       {
          private Book[] books;      
          
          #region ctors
          internal Library(Book[] bookArray)
          {
             /* So steht es zwar im MSDN-Sample,
              * aber das muß gar nicht so sein! */
             //books = new Book[bookArray.Length];
             //for(int i = 0; i < bookArray.Length; i++)
             //{
             //   books[i] = bookArray[i];
             //}
    
              /* Es geht auch so: */
             books = bookArray;
          }      
          #endregion      
                
          #region IEnumerable Members
          // Wird als erstes von der foreach-Schleife aufgerufen:
          public IEnumerator GetEnumerator()
          {
             return new BookEnumerator(books);
          }
          #endregion
       }
    
       class BookEnumerator : IEnumerator
       {
          private int counter = -1;
          internal Book[] books;
    
          internal BookEnumerator(Book[] list)
          {
             books = list;
          }
    
          #region IEnumerator Members
          // Wird als drittes von der foreach-Schleife aufgerufen:
          public object Current
          {
             get
             {
                try
                {
                   return books[counter];
                }
                catch(IndexOutOfRangeException)
                {
                   throw new InvalidOperationException();
                }
             }
          }
    
          // Wird als zweites von der foreach-Schleife aufgerufen:
          public bool MoveNext()
          {
             counter++;
             return (counter < books.Length);
          }
    
          // Wird von einer foreach-Schleife nicht benötigt:
          public void Reset()
          {
             counter = -1;
          }
          #endregion
       }
    
       class Program
       {
          static void Main()
          {
             Book[] bookList = new Book[2]
             {
                new Book("Erstes Buch", 1.00m),
                new Book("Zweites Buch", 2.00m)
             };
             Library libraryList = new Library(bookList);
             foreach(Book item in libraryList)
             {
                Console.WriteLine(item.Name + " / " + item.Price.ToString());
             }         
    
             // Verhindert das selbsttätige Schliessen des Konsolenfensters.
             Console.WriteLine("\nBeenden mit Return");
             Console.ReadLine();
          }
       }
    } 

    Codediskussion -> was im einzelnen passiert

    Es fängt mit der Klasse Book an, die ein passives Objekt mit mit zwei Eigenschaften (Name und Preis) verkörpert.
    Hinweis: Die hier abgebildeten Properties sind in C#3 definiert.

    class Book
    {
       internal Book(string name, decimal price)
       {
          Name = name;
          Price = price;
       }
    
       internal string Name { get; private set; }
       internal decimal Price { get; private set; }
    }

    Von diesem Objekt wird ein Array vom Typ Book und mit zwei Instanzen gebildet. Hier zu sehen ist die C#3-Variante dieser Arraybildung.

    Book[] bookList = new Book[2]
    {
       new Book("Erstes Buch", 1.00m),
       new Book("Zweites Buch", 2.00m)
    };

    Als nächstes wird eine Klasse Library instanziiert, die das IEnumerable-Interface implementiert. Im Konstruktor wird das Buch-Array übergeben.

    Library libraryList = new Library(bookList);

    Die Instanziierung im Einzelnen:
    Die Klasse Library beherbergt eine private Variable vom Typ Book. Über den Konstruktor wird darin eine Kopie des Book-Arrays abgelegt.

    class Library : IEnumerable
    {
       private Book[] books;
       #region ctors
       internal Library(Book[] bookArray)
       {
          books = bookArray;
       }
       #endregion
       // Weiterer Code der Klasse Library,
       // der aber zu diesem Zeitpunkt noch keine Rolle spielt.
       //...  

    Die Instanziierung ist damit abgeschlossen. Als nächstes folgt eine foreach-Schleife:

    foreach(Book item in libraryList)
    {
       Console.WriteLine(item.Name + " / " + item.Price.ToString());
    }

    Diese macht nun Gebrauch von den Methoden, die durch die beiden Interfaces implementiert wurden.
    Zuerst ruft foreach GetEnumerator() auf.

    // Gehört zur Klasse "Library":
    #region IEnumerable Members
    // Wird am Anfang der foreach-Schleife aufgerufen:
    public IEnumerator GetEnumerator()
    {
       return new BookEnumerator(books);
    }
    #endregion

    Damit wird das Book-Array an den Konstruktor der Klasse BookEnumerator übergeben.
    Danach wird eine Instanz dieser Klasse an den Aufrufer (hier die foreach-Schleife) zurückgegeben.
    Der Rückgabetyp IEnumerator dient als Filter. Egal welche Objekte man noch in die Klasse BookEnumerator hinein strickt, die zurückgegebene Instanz beinhaltet nur die von IEnumerator implementierten Objekte.

    class BookEnumerator : IEnumerator
    {
       private int counter = -1;
       internal Book[] books;
    
       internal BookEnumerator(Book[] list)
       {
          books = list;
       }

    Hier fällt auf, daß die Variable counter einen Wert von -1 erhält. Warum das so ist, sehen wir jetzt.
    Es geht weiter in der foreach-Schleife. Diese ruft als nächstes die Methode MoveNext() auf:

    // Wird als zweites von der foreach-Schleife aufgerufen:
    public bool MoveNext()
    {
       counter++;
       return (counter < books.Length);
    }

    Bevor irgend etwas aus dem Array ausgelesen wird, wird der Zähler um 1 inkrementiert.
    Darum muß die Zählervariable am Anfang also immer VOR der ersten Zelle des enumerierbaren Objekts stehen!
    Die foreach-Schleife hat nun einen Pointer, der auf die aktuelle Zelle des Arrays zeigt. MoveNext() gibt aber nicht diesen Zähler zurück. Dieser wird nur klassenintern gespeichert. Statt dessen gibt die Methode true oder false zurück, je nachdem ob sich der Zähler noch innerhalb der Arraygrenzen befindet. Daran erkennt foreach, ob noch ein weiterer Schleifendurchlauf folgen muß, oder ob das Ende erreicht ist.
    Als nächstes holt die Schleife den Wert, der am Pointer steht. Das geschieht durch Abruf des Properties Current:

    // Wird als drittes von der foreach-Schleife aufgerufen:
    public object Current
    {
       get
       {
          try
          {
             return books[counter];
          }
          catch(IndexOutOfRangeException)
          {
             throw new InvalidOperationException();
          }
       }
    }

    Wie man hier sieht, wird der Wert an der Pointerposition der books-Instanz zurückgegeben. Wir erinnern uns: Diese Instanz wurde durch den Aufruf von GetEnumerator() im Konstruktor der Klasse BookEnumerator gebildet und ist eine Kopie unseres Book-Arrays.
    MoveNext() und Current werden mit jedem Schleifendurchlauf aufgerufen.

    Zum Schluß stehen noch zwei Fragen im Raum:

    1. Wofür wird die durch das IEnumerator-Interface implementierte Methode Reset() benötigt?
      Klassen die das IEnumerator -Interface implementieren, kann man nicht nur mit foreach benutzen, sondern auch quasi manuell. Für alle Eventualitäten steht diese Methode zur Verfügung.
    2. Warum werden zwei Interfaces benutzt? Hätte Microsoft das nicht anders lösen können? Es sieht doch so aus, als würde die Methode GetEnumerator() nur eine Referenz auf die Enumeratorklasse zurück geben. So ist es auch in der Tat. Warum also dieser Umweg? Könnten die Enumerationsobjekte nicht gleich in die Library-Klasse implementiert werden? Theoretisch schon, nur wird hier die Objektorientierung berücksichtigt. Die Bibliothek (Library) hat nicht die Aufgabe, durch eine Buchliste zu blättern. Dafür ist jemand anderes (BookEnumerator) zuständig.

    Schlußwort

    Dieser Artikel spiegelt mein eigenes Verständnis wider. Es muß sich nicht zwingend mit dem offiziellen Verständnis decken. Ich kann vollkommen falsch liegen! Für konstruktive Kritik bin ich dankbar.

    March 05

    Events in C#, ein einfaches Beispiel

    Das Thema Events ist gerade für Beginner schwer verständlich. Ich spreche da aus eigener Erfahrung. Hier nun ein Beispiel das alles zeigt was für ein Event und Eventhandling nötig ist - ohne verwirrenden Schnickschnack.

    using System;
    
    namespace EventSimple
    {
       public class TestClass
       {
          // Der Delegat muß die gleiche Signatur aufweisen wie
          // die Eventhandler-Methode.
          public delegate void EventDelegate();
          // Das Event-Objekt ist vom Typ dieses Delegaten
          public event EventDelegate MyEvent;
    
          public void OnEvent()
          {
             // Prüft ob das Event überhaupt einen Abonnenten hat.
             if(MyEvent != null)
                MyEvent();
          }
    
          // Exemplarische Methode,
          // die unter einer bestimmten Bedingung ein Event auslöst.
          public void Observable()
          {
             Random rnd = new Random();
             int result = rnd.Next(2);
             // Das Event wird gefeuert wenn result ungleich 0 ist.
             if(result != 0)
                OnEvent();
          }
       }
    
       class Program
       {
          public static void Main()
          {
             TestClass tc = new TestClass();
             // Hier wird das Event abonniert.
             // Das kann jede Klasse auf die gleiche Weise machen.
             tc.MyEvent += new TestClass.EventDelegate(EventHandler);
             tc.Observable();
          }
    
          static void EventHandler()
          {
             Console.WriteLine("Event wurde ausgelöst.");
          }
       }
    }

    image

    March 04

    Lookup-Wert aus einer n:m Beziehung holen

    Eine DataGridView soll befüllt werden. Zu Grunde liegen (unter anderen) die Tabellen

    • Book
      • ID (PK)
      • Title
      • FK_Category, Fremdschlüssel für den PK der Tabelle Category (1:n Beziehung)
      • ISBN
      • Price
      • ... (weitere)
    • BookThemes (Crosstable)
      • ID (PK),
      • FK_Book, Fremdschlüssel für den PK aus der Tabelle Book
      • FK_Theme, Fremdschlüssel für den PK aus der Tabelle Themes
    • Themes
      • ID (PK)
      • Theme

    Ein Buch kann mehrere Themen behandeln, zu einem Thema gibt es aber auch viele Bücher - es handelt sich also um eine n:m Beziehung. In der Tabelle Book gibt es keine Referenz auf ein Thema. Der Zusammenhang zwischen beiden ist nur in der Crosstable sichtbar. Auch der O/R-Mapper (DBML-File) kann keine Wunder vollbringen. Eine LINQ2SQL-Expression muß hier also etwas komplexer ausfallen. Hier ist die Lösung (BookManagementDataContext ist die vom dbml-Designer erzeugte Mapper-Klasse):

    private void PopulateDataGridView()
    {
    BookManagementDataContext bmdc = new BookManagementDataContext(); var books = from book in bmdc.Books // Dieser Abschnitt holt das Thema ->> from bt in bmdc.BookThemes where bt.Fk_Book == book.ID from theme in bmdc.Themes where theme.ID == bt.Fk_Theme // <<- select new { Titel = book.BookTitle, Kategorie = book.Category.CategoryName, Thema = theme.ThemeName, ISBN = book.ISBN, Preis = book.Price, Eigentümer = book.Owner.OwnerName, }; dataGridView1.DataSource = books; }

    Anmerkungen:

    • Diese Where-Klauseln entsprechen der generellen SQL-Syntax für join-Operationen (also nicht Microsoft-spezifisch). Den gleichen Effekt bekäme man also auch über entsprechende Microsoft-joins.
    • Der Mapper ist in der Lage, eine 1:n Beziehung wie bei Kategorie und Buch aufzulösen. Diese Relation muß nicht erst in der LINQ-Expression aufgelöst werden.
    March 03

    Erste Erfahrungen mit XAML

    Einbindung eines Namespaces in ein XAML-Form

    Für eine Demo der ValueConversionAttribute-Klasse hatte ich eine eigene Klasse geschrieben und ihr den Namespace ValueConversionDemo gegeben.
    Nun wollte ich diesen in ein XAML-Form einbinden:
    Dabei gab es direkt einen Error:

    clip_image001 

      Auf Deutsch, der Namespace konnte nicht gefunden werden. Die Lösung ist so einfach wie bescheuert:
      VS versucht hier eine existente Assembly zu referenzieren, statt wie gewohnt in den anderen Codefiles nachzuschauen. Keine Ahnung warum das so ist.
      Man löst das Problem, indem man das Projekt einmal compilieren lässt.
      Fortsetzung folgt...