Peter Bucher - Mein Experiment, meine Spielereien, meine Welt...   ·   Stefan Falz   ·   Jürgen Gutsch   ·   Golo Roden   ·   ASP.NET Zone   ·   Microsoft ASP.NET
Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Teil 1: Umkehrung der Kontrolle erklärt, oder: Von der Fabrikmethode zum DI-Container

Dies ist der Beginn der Artikelserie "Umkehrung der Kontrolle erkärt, oder: Von der Fabrikmethode zum DI-Container" mit folgenden Teilen:

LightCore gibt es schon seit längerer Zeit, andere DI-Container noch länger.
In dieser Blogpostreihe geht es aber nicht primär um LightCore, auch nicht um die anderen DI-Container.

Ich möchte mit diesem Blogpost versuchen zu erkären, wie denn die ganze Geschichte funktioniert.

Vom Ursprung bis zu einer implementierten Komponente.

Das Feedback zur LightCore-Webseite und LightCore selber hat auf alle Richtungen ausgeschlagen.

Von: Wow, cool. Brauche ich!, zu: Was ist das denn?, bis zu: Braucht man sowas?, über: Sowas braucht man nicht!, bis zu: Mache ich von Hand oder in 10 Zeilen!.

An dieser Stelle sei gesagt: Ich würde die Fragen nach dem "brauchen" in jedem Fall mit "Ja" beantworten, je nach Kontext wo denn etwas eingesetzt werden soll.

Die Grundprobleme:

  1. Abhängigkeit zu konkreten Typen
  2. Redundanz bei der Konstruktion von Typen jeglicher Art
  3. Keine einfache Möglichkeit der Austauschbarkeit von Typen
  4. Keine einfache Möglichkeit der Erweiterbarkeit über Typen

Wieso überhaupt "Abhängigkeit lösen"?

Um die Probleme 2, 3 bis 4 zu lösen. Das heisst, die möglichen Redundanzen - also bspw. doppelten Instanziierungen von Klassen - zu vermeiden. Durch das Lösen der Abhänigigkeit von einem konkreten zu einem abstrakten Typen können die Typen ausgetauscht werden und eine Erweiterung ist mit weniger Aufwand möglich.

Die Abhängigkeit wird nicht "verbannt", sondern gelöst. Lösen heisst in diesem Fall, dass die Abhängigkeit nicht mehr auf _einen_  _konkreten_ Typen besteht, sondern auf _eine_ Abstraktion, die _mehrere_ unterschiedliche Implementationen beinhalten kann.

Anstelle dass nur "World" genutzt werden kann, kann dann mithilfe der Abstraktion "WorldBase", "SecondWorld", "IrgendwasWorld", etc... benutzt werden.

Diese Grundprobleme können auf verschiedene Arten gelöst werden. Vorerst die einfachsten Möglichkeiten.

Eine einfache Abhängigkeit zu konkreten Typen kann folgendermassen aussehen:

World world = new World();
world.SayHello();

 

In diesem Beispiel ist die Methode "SayHello" von der konkreten Klasse "World" abhängig.

Wollen wir diese Abhängigkeit lockern / lösen, brauchen wir eine Abstraktion irgend einer Art.
Das kann ein Interface sein, eine Abstrakte Klasse oder auch eine einfache konkrete Oberklasse.

Am Beispiel einer abstrakten Klasse:

WorldBase world = new World();
world.SayHello();

 

Ja, das war erst der erste Schritt. Das Problem besteht immer noch, es liegt in "new World();". Wir referenzieren den Konstruktur der konkreten Klasse.
Die einfachste Lösung hierfür ist eine Fabrikmethode (Factory Method), womit die Konstruktion der Instanz ausgelagert wird.

Dies könnte so aussehen:

public static WorldBase ConstructMyWorld()
{
                return new World();
}

WorldBase world = ConstructMyWorld();
world.SayHello();

 

So ist das Problem auf einfachste Art gelöst.

Zusammenfassung:
Das Problem war die Abhängigkeit auf die konkrete Klasse "World".
Wenn wir anstelle von der Klasse "World", "SecondWorld" benutzen wollen, brauchen wir das jetzt nur noch an der Stelle "return new World" zu ändern, in "return new SecondWorld();"

Wie häufig wir die Fabrikmethode "ConstructMyWorld" benutzen, bleibt uns überlassen.
Die Stelle bleibt, ein Ort der Änderung.

Das wird möglich indem wir eine Abstraktion benutzen, in unserem Falle "WorldBase".
Die konkreten Klassen "World", "SecondWorld" müssen von aussen gleich aussehen (Implementieren die gleiche Abstraktion), können allerdings anders implementiert sein.

Die Angabe des Typs "WorldBase" braucht bei der Zuweisung am Ort des Benutzens angegeben werden, sowie auch als Rückgabetyp der Fabrikmethode.

Das war die einfachste Möglichkeit der Umkehrung per Fabrikmethode.

Die Frage bleibt evt. noch: Wieso Umkehrung?

Am Anfang hatte die Zuweisung am Ort des Benutzens die Kontrolle über den konkreten Typen, sie gab also an, was sie möchte.

Jetzt liegt die Kontrolle _in_ der Fabrikmethode. Der Ort des Benutzens weiss nur, das da ein Typ zurückkommt, der aussieht wie "WorldBase", nicht mehr und nicht weniger.

Am Schluss noch der vollständige (vermutlich nicht sofort lauffähige = Pseudo) Code:

public class ConsoleApplication
{
                public static void Main()
                {
                                WorldBase world = ConstructMyWorld();
                                world.SayHello();
                }

                public static WorldBase ConstructMyWorld()
                {
                                return new World();
                }
}

public abstract class WorldBase
{
                public abstract void SayHello();
}

public class World : WorldBase
{
                public void SayHello()
                {
                               Console.WriteLine("Hello World");
                }
}

public class SecondWorld : WorldBase
{
                public void SayHello()
                {
                               Console.WriteLine("Hello SecondWorld");
                }
}

See# Party - Die Community Konferenz am Bodensee

Jürgen Gutsch und ich haben bekanntlich ja den grenzübergreifenden (CH / DE) .NET Stammtisch Konstanz-Kreuzlingen gegründet.
Ich bin leider nicht mehr dort lokalisiert aber natürlich immer noch sehr daran interessiert, das es gut läuft.

Das neue Team rund um Jürgen Gutsch und Tilo Schinke hat eine riesen Arbeit geleistet und stellte in kurzer Zeit eine Konferenz in Kreuzlingen (CH) auf die Beine.
Dabei sind sehr viele bekannte Sprecher, wie beispielsweise Stefan Lieser, Albert Weinert, Ken Cascada uvm, die sehr interessante Vorträge halten werden.

Offizielle Ankündigung:

Der .NET-Stammtisch Konstanz-Kreuzlingen veranstaltet am 28.08.2010 die .NET-Community Konferenz  See# Party in Kreuzlingen am Bodensee. Eine Konferenz von und für die .NET Community und interessierte in der Schweiz und Deutschland.

Diese Konferenz bietet den Teilnehmern viele interessante Vorträge aus dem .NET Umfeld, vorgetragen von Entwicklern aus der .NET-Community.

Die Anmeldung für bis zu 250 Teilnehmer startet in Kürze

Ziel ist es am Bodensee, für die Schweiz und Deutschland, eine Konferenz von und für die .NET Community und interessierte zu Veranstalten.

Die See# Party soll auch eine günstige und nahe alternative zu den großen Konferenzen darstellen.

Es würde mich freuen, euch dort zu sehen! :-)

LightCore 1.4 Release

*Update: Die Quellcode Lizenz von LightCore wurde auf MS-PL umgestellt und die aktuelle Version ist inzwischen 1.4.1.

Es hat eine Weile gedauert, doch nun ist es soweit: LightCore ist in der Version 1.4 verfügbar und die Website glänzt mit neuem Design und Inhalt.

Mit diesem Release wurden bekannte Fehler aus der Version 1.0 behoben, intern viel verändert und bietet natürlich auch neue Features.

Silverlight 3.0 und Compact Framework 3.5 Unterstützung wurden gefordert, diese Forderung wurde umgesetzt und zusätzlich findet sich jetzt auch eine .NET 4.0 Variante, die den neuen Lazy<T>-Typ integriert.

Unter anderem wurde folgendes geändert:

  • Interne Änderungen, mehr UnitTests, Optimierungen, Fehlerbehebungen.
  • Open Generic Unterstützung per XML-Registrierung.
  • Unterstützung von mehreren, typisierten Argumenten pro Registration mit XML oder per Code.
  • Bessere Fehlermeldungen und Fehlerbehandlung.
  • Viel mehr Features durch die RegistrationSources, bspw. Factory- oder Array-RegistrationSource.
  • Unterstützung von ASP.NET MVC 2.0.
  • Property Injection Performance verbessert.
  • Benannte Registrierungen sind ab dieser Version nicht mehr unterstützt.
  • Compile- und Laufzeit-Argumente in verschiedener Form (XML, anonymer Typ, IDictionary<string, object>,Argument beim Resolve Aufruf).

LightCore ist unter http://lightcore.ch/ erreichbar.

An dieser Stelle möchte ich Jürgen Gutsch, Gregor Biswanger, Dennis Bischof, Golo Roden und Roberto Bez danken, die mir während der Entwicklung und für die Dokumentation von LightCore stets mit Rat und Tat zur Seite stehen.

Was kostet LightCore?

Die Binaries von LightCore dürfen in jeglicher Art von Projekten (privat, kommerziell, ...) kostenlos genutzt werden.
Der Quellcode von LightCore steht unter der MS-PL-Lizenz.

Eingetragen von Peter Bucher | 1 Kommentare
Abgelegt unter: , , , ,

Blogroll geräumt

Ab und zu überprüfe ich die Links in meinem Blogroll.
Dabei kann es sein, das die Seite ganz weg ist, oder der Link inzwischen falsch ist.

Da ist eine Nachforschung und Ersetzung des Links relativ einfach.
Und alle anderen die jetzt rausgeflogen sind, hatten entweder keinen Blogroll oder
mein Blog nicht mehr verlinkt.

Wenn ich jemand ohne Verschulden rausgeworfen habe, oder jemand denkt, er gehört noch rein,
dann bitte melden.

Am einfachsten hier: http://www.peterbucher.ch/kontakt.aspx

Eingetragen von Peter Bucher | 0 Kommentare
Abgelegt unter: , ,

Anonymen Zugriff für VisualSVN Server konfigurieren

Schon seit längerem betreibe ich einen SVN-Server mithilfe der kostenlosen Software VisualSVN Server.
Leider kann standardmässig kein anonymer Zugriff (Ohne Logindaten) genutzt werden, es braucht mindestens einen Namen bspw. "guest" oder "anonymous".

Mit folgender Anleitung von Rick Strahl ist es einfach möglich, nach einer kleiner Änderung in der Konfiguration dem Spezialbenutzer "Everyone" Rechte zu geben. Das gilt dann für Clients, die keine Logindaten angeben.

Office 2010 installiert und Visual Studio 2008 friert ein?

Nebenbei habe ich letztens Office 2010 installiert.
Als ich dann wieder einmal Visual Studio 2008 benutzte, blieb die Anwendung im ASPX-Editor komplett hängen.

Es musste der Prozess abgeschossen werden, um VS überhaupt beenden zu können.
Mein Verdacht lag zuerst bei Resharper, was sich dann aber als falsch herausgestellt hat.

Bei mir hat die Lösung mit dem Reparieren von den "Web Authoring Components" geholfen:

Windows Photo Gallery Gelbstich im Hintergrund bei diversen TFT Monitoren

Wer im Hintergrund von Windows Photo Gallery (Foto Vorschau von Windows) einen Gelbstich im Hintergrund hat, kann dies durch ein anderes Farbprofil ausbessern.

Speziell Samsung Monitore scheinen dafür anfällig zu sein. Bei mir hat die erste Lösung geklappt:

Erster Workaround:

The first workaround is by removing any existing ICC or WCS color profiles that are been associated with all display devices or monitors. Else, you can also try to change the default color profile for your system’s monitor to sRGB IEC61966-2.1, simply by clicking on Add, and then select sRGB IEC61966-2.1 from the list of profiles installed on system. After adding, click Set as Default Profile button. Exit from all dialogs and reboot your computer, and the color problem on Windows Photo Gallery is fixed.

Zweiter Workaround:

If your system doesn’t have sRGB IEC61966-2.1 profile under the ICC Profiles, you can download the color profile from Adobe. Unpack the zip file, the right click on each of the .icc files in the RGB Profiles and CMYK Profiles folders (or simply just the one you need to use) and click “Install Profile” on context menu to install the color profile to system.

Quelle: mydigitallife.info

Abstraktion

Am 13. Oktober 2008 haben Golo Roden und ich unter dem Titel Noch Fragen, Roden? Ja, Bucher! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen.

Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. März 2010, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Abstraktion

So wohl Golo wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Außerdem nimmt diesen Monat auch Roberto Bez an unserem Streitgespräch teil.

Golos und Robertos Kommentare finden sich zeitgleich in den entsprechenden Blogs, folgend nun mein Kommentar zu diesem Thema:

Der Begriff “Abstraktion” wird in vielen möglichen Bereichen genutzt, sei dies Kunst, Psychologie, Philosophie, Mathematik oder sogar in der Sprachwissenschaft.

Abstraktion hat in jedem Bereich die gleiche Bedeutung, jedoch in einem jeweils anderen Kontext.

 

Ein Beispiel aus der Psychologie:

In einem Leben wird der Mensch mit sehr viel Wissen, Tatsachen und Situationen auseinandergesetzt.
Wenn der Mensch alles im Kopf behält und das auch im Leben nutzt, wäre das Leben um einiges komplizierter.

Bspw. am Mittagstisch, entweder “Gib mir bitte die Butter” vs. “Gib mir bitte die Laco Butter mit 5% Fettanteil, die Gestern Aktion war.”

Bei zweiterem müsste der Gegenüber wohl zuerst mal überlegen, was überhaupt gefordert ist oder ob es evt. sogar ein Scherz ist.
Danach schauen ob all die Eigenschaften übereinstimmen und dann schlussendlich die Butter bringen oder eine Antwort geben.

Ihr könnt es schon denken: Das Leben wäre viel zu kompliziert.
Mit ersterem Satz weiss jedes Kind was gemeint ist und die Komplexität nimmt rapide ab.

Man kann schlussendlich immer noch sagen: “Bitte gib mir die light Butter” / “Bitte gib mir die normale Butter”, wenn eine solche Unterscheidung notwendig ist, wenn zwei Butter vorhanden sind.

Das Beispiel zu Kunst auf Wikipedia ist auch sehr interessant: http://de.wikipedia.org/wiki/Abstraktion#Kunst

Es geht also um die Generalisierung, um das weglassen von Details, wobei genau dasselbe erreicht werden kann.

 

In der Software-Entwicklung gibt es zum einen die Objektorientierte Programmierung und zum anderen Sprachkonstrukte für Generalisierung.
Auch wenn es wohl einige nicht daran denken, weil sie zufest in abstrakte Klassen und Interfaces eingefahren sind: Auch konkrete Klassen sind Abstraktionen.

Sie verbergen Details (Datenkapselung), die ja eigentlich dazugehören, aber den Client nicht interessiert, sowie einen Schutz darstellt, wenn das Klasse später noch geändert werden muss, ohne das der Clientcode bricht.

Auch bei konkreten Klassen gibt es die Generalisierung und Spezialisierung. “String” ist eine Spezialisierung vom Typ “object” und “Person” eine Generalisierung von “PoliceOfficer”.

Zusätzlich zu den Möglichkeiten der Objektorientierung gibt es in C# zwei Sprachkonstrukte, um die Nutzung von Abstraktion noch effektiver zu nutzen.

Abstrakte Klassen und Interfaces, mehr dazu könnt ihr in einem frühreren Streitgespräch lesen:

Felder vs Eigenschaften

Am 13. Oktober 2008 haben Golo Roden und ich unter dem Titel Noch Fragen, Roden? Ja, Bucher! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen.

Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. Februar 2010, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Felder vs Eigenschaften

So wohl Golo wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Golos Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:

An mehreren Orten wird debattiert, ob es nun besser sei eine öffentliche Eigenschaft, oder öffentliches Feld zu benutzen.
Teilweise gehen die Meinungen Richtung KISS oder YAGNI, was auch teilweise nachvollziehbar war, als es noch keine automatischen Eigenschaften gab.

Eine Eigenschaft ist jedoch nicht dasselbe wie ein Feld, es hat eine andere Bedeutung.
Während ein Feld nur ein einfacher Datencontainer darstellt, also eine Variable auf Instanzebene, repräsentiert eine Eigenschaft eine Schnittstelle zu Daten jeglicher Art.
Eine Eigenschaft kann bspw. auch Daten aus mehreren Feldern und noch einer zusätzlichen Berechnung haben.

Die Implementierung einer Eigenschaft kann ohne Bedenken geändert werden, ohne das ein Benutzer neu kompilieren muss, es besteht also eine binäre Kompatibilität.
Das ist ein nicht zu unterschätzender Vorteil gegenüber Feldern, der es durchaus Wert ist, mehr Schreibarbeit und Code auf sich zu nehmen.

In den Framework Design Guidelines von Microsoft steht unter anderem auch:

Do not use instance fields that are public

Zu all diesen Vorteilen kommt noch hinzu, das beim Databinding explizit öffentliche Eignschaften gefordert sind.

Der aus meiner Sicht einzige Vorteil von öffentlichen Feldern ist Geschwindigkeit, sie sind definitiv schneller.
Allerdings sollte sowas - nur wenn nötig (Siehe auch Speedfreak) - auch nur in einer internen API so genutzt werden, die unter eigener Kontrolle steht.

Werden alle Vorteile der Eigenschaften zusammengezogen und ist keine Optimierung nötig, sollte immer eine Eigenschaft anstelle eines öffentlichen Feldes genutzt werden.

PS: Das Buch Framework Design Guidelines ist sehr zu empfehlen!

Eingetragen von Peter Bucher | 3 Kommentare

ASP / ASP.NET MVP 2010

Anfang Jahr erhielt ich eine Email von Microsoft, das ich für das Jahr 2010 renominiert wurde :)

Das freut mich ungemein und ich möchte mich bei den Betreibern und Moderatoren von ASP.NET Zone und myCSharp bedanken.
Diese Auszeichnung durfte ich jetzt schon das vierte Mal in Folge entgegennehmen, wobei 4 meine Lieblingszahl ist :)

Sicherlich finde ich die Zeit auch mal in Konstanz beim .NET Stammtisch Konstanz / Kreuzlingen vorbeizuschauen und einen Vortrag zu halten.

Auf ein erfolgreiches Jahr 2010!

Eingetragen von Peter Bucher | 5 Kommentare
Abgelegt unter: , , ,

this oder kein this

Am 13. Oktober 2008 haben Golo Roden und ich unter dem Titel Noch Fragen, Roden? Ja, Bucher! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen.

Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. Januar 2010, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

this oder kein this

So wohl Golo wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Golos Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema.

Generell ist es sinnvoll, wenn ohne weiteres Zutun gleich erfasst werden kann, ob es sich um eine lokale, statische oder instanzbehaftete Variable handelt.
Aus diesem Grund gibt es Konventionen, wie etwas auszusehen hat, sei dies mit Pascal- (fooBar) / Camel-Casing (FooBar) oder durch Prä- / Postfixe (m_foo, foo_m).

Im Allgemeinen hat es sich bei mir so entwickelt, dass ich mich immer mehr zu der empfohlenen Microsoft Konvention hin bewegt habe, was die Einarbeitung in andere Projeke um einiges leichter macht,
da diese Konvention häufig – wenn auch teilweise abgeändert – verwendet wird.

Im aktuellen Streitgespräch geht es darum, ob this verwendet wird / nicht verwendet wird und wo es verwendet wird.

this wird zwanghaft benötigt, um auf die Referenz auf sich selber, in die Hand zu bekommen.
Beispielsweise wenn in einer User-Klasse eine .Save-Methode angeboten wird, die ihrerseits aber einen Service benutzt um sich speichern zu lassen:


public class User
{
    public void Save()
    {
        ServiceLocator
                .Resolve<IUserService>()
                .Save(this);
    }
}

Erweiterungsmethoden benutzen this als Kennzeichnung:


static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (var item in source)
    {
        action(item);
    }
}

Bei einer Indexerdeklaration wird this auch benötigt:

public User this[sring index]
{
   get { return this._list[index]; }
   set { this._list[index] = value; }
}

Ein andere Stelle wo this benutzt werden muss, ist beim angeben von zusätzlichen Konstruktoren von sich selbst, die auch noch aufgerufen werden sollen:


public class UserService
{
    private readonly IRepository<User> _userRepository; 

    public UserService() : this(new XmlUserRepository())
    {
       
    }
    public UserService(IRepository<User> userRepository)
    {
         _userRepository = userRepository;
    }
}

Im ersten Konstruktor wird angegeben, das auch der zweite Konstruktor mit einem Parameter aufgerufen werden soll.

Wenn die Instanzvariable “_userRepository” keinen Unterstrich hätte – also “userRepository” – müsste this verwendet werden, damit der Kompiler zwischen Instanzvariable und Argument unterscheiden kann, also:


private readonly IRepository<User> userRepository;
public UserService(IRepository<User> userRepository)
{
    this.userRepository = userRepository;
}

Zu Anfang habe ich das Präfix “_” vor einer Instanzvariable benutzt, so kann man – auch im Intellisense – ohne Probleme auswählen und unterscheiden, da alle schön gruppiert sind.

In Visual Studio 2005 gab es allerdings kein Intellisense bei der Eingabe von “_test” im Code-Editor.
Ich habe dort “this._test” genutzt, denn es ist ja ein Instanzmember und mit der Eingabe von “this.” gab es dann Intellisense.

Ab Visual Studio 2008 gibt es bereits bei der Eingabe von “_” Intellisense, somit wäre “this” überflüssig. Allerdings habe ich mir angewöhnt, this überall anzugeben, wo sich etwas auf die Instanz bezieht.

Also für Methodenaufrufe auf der Instanz (in der Regel protected oder private Methoden), Eigenschaften sowie Instanzfelder.
Es ist so möglich auf den ersten Blick zu unterscheiden und erfassen, was eine lokale Variable ist, was ein statisches Feld / Konstante. Oder doch eine Instanzvariable?.

Was meint ihr dazu?

Eingetragen von Peter Bucher | 1 Kommentare
Abgelegt unter: , ,

InternalsVisibleToAttribute und signierte Assemblies: So funktionierts garantiert.

Stellt euch vor, ihr habt zwei Assemblies:

  • MyProject.Core
  • MyProject.Core.Tests

Im Core-Projekt gibt es Typen, die nicht öffentlich gemacht werden sollten, allerdings für die UnitTests zugänglich sein sollen.
Nun, das ist ein kleines Problem, wenn man das Attribut “InternalsVisibleTo” kennt, mithilfe folgender Dekleration in der AssemblyInfo.cs des MyProject.Core-Projekts kann dies einfach erledigt werden:

[assembly: InternalsVisibleTo("MyProject.Core.Tests”)]


Das macht sogar sehr viel Sinn, denn sonst können Kollisionen und Verwechslungen der Extensionmethods beim Endbenutzer vorkommen.

Soweit, so gut. Die Typen der MyProject.Core-Assembly sind jetzt in der MyProject.Core.Tests-Assembly sichtbar.
Müssen die Projekte jedoch signiert sein, kommt es zu einem grösseren Problem.

In mehreren Blogs sind die nötigen Schritte beschrieben, jedoch werden da wichtige Punkte nicht genannt, die einem zur Verzweiflung treiben können.
Die sind wie folgt:

  • Alle involvierten Assemblies müssen signiert werden.
  • Der PublicKey muss vom Projekt bezogen werden, bei dem die Internals sichtbar werden sollen, in unserem Fall also das MyProject.Core.Tests-Projekt.

Nach einer weiteren Suche bin ich schlussendlich auf einem (selbsternannten) Newbie-Blog fündig geworden.
Noch später sties ich dann auf die folgende Anleitung, die eigentlich alles beinhaltet, sodass ich nicht alles im Detail niederschreiben muss, trotzdem die wichtigen Punkte nennen kann und vorallem die Lösung wieder einmal finde:

Also, beachtet die oben stehenden zwei Punkte und haltet euch an folgende Anleitung:

Eingetragen von Peter Bucher | 3 Kommentare

Reflection – Fluch oder Segen?

Am 13. Oktober 2008 haben Golo Roden und ich unter dem Titel Noch Fragen, Roden? Ja, Bucher! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen.

Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. Dezember 2009, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Reflection – Fluch oder Segen?

So wohl Golo wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Golos Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:

 

Reflection?

Reflection, Reflexion bzw. Introspektion macht es möglich, Programme wie Daten zu behandeln und so Informationen über Typen / Member abzurufen und auch zu setzen.

Eine Einführung zu Reflection findet sich bspw. hier, und auf myCSharp.de findet sich ein super ausführlicher und tiefgehender Artikel, der auf jeden Fall lesenswert ist.
Darüber hinaus ist es per Reflection möglich, eine späte Bindung in jeglicher Hinsicht zu erreichen. Sei dies das dynamische Laden einer Assembly oder auch das dynamische Aufrufen einer Methode zur Laufzeit.
Damit lassen sich Service Locator und Dependency Injection Container - also Systeme zur Entkoppelung - umsetzen, die je nach Konfiguration eine andere Assembly mitsamt ihren Typen lädt.

Ein praktischer Anwendungsfall wäre ein Serialisierungsmechanismus, der Objekthierarchien bspw. in XML-Form abspeichern kann.
Mithilfe von Reflection können die zu speichernden Typen in Form einer Instanz von System.Reflection.Type abgefragt werden.

Ein Type Objekt macht alles verfügbar, was zu einem Typen gehört, seien dies Methoden, Attribute, Eigenschaften oder gar Felder.

Kleines Beispiel:

int count = 2012;
Type typeOfCount = count.GetType(); // Oder: typeof(int)
Console.WriteLine(typeOfCount);

Ausgabe ist die Stringrepräsentation des Types der mit dem Alias int verknüpft ist, also: “System.Int32”.

Schluss ist damit noch lange nicht, es ist genau so möglich Code zur Kompile- oder Laufzeit dynamisch zu erstellen.
Das .NET Framework bietet die hierzu nötigen Klassen im Namensraum System.Reflection.Emit bereit.

Die Anwendung dieser API ist jedoch relativ umständlich und komplex, dafür extrem mächtig.

 

Segen?

Reflection und Bibliotheken die darauf aufbauen birgen ein riesen Potential und ungeahnte Möglichkeiten.
Die Anwendung ist extrem einfach und viele Features im .NET Framework selber bauen auch auf Reflection auf.

Also auf jeden Fall ein Segen… auf der einen Seite.

 

Fluch?

Auf der anderen Seite kann es auch zu einem Fluch werden, denn mit Reflection kann viel Humbug getrieben werden.
Zum einen ist die Ausführung von Code der Reflection nutzt, um einiges langsamer, was zu Performanceproblemen führen kann, zum anderen kann so auch die Mauer der Zugriffsmodifizierer umgangen werden, da es ohne viel Aufwand möglich ist, auch private Members einer Klasse auszulesen.

Zudem können Probleme auftreten, wenn die Umgebung in der eine Anwendung läuft, zuwenig Rechte hat. Das kann dazu führen, das eine Anwendung überhaupt nicht, oder nur eingeschränkt lauffähig ist, in restriktiven Umgebungen.

Über das dynamische Laden von Assemblies kann es zu Sicherheitsproblemen kommen, falls die geladenen Assemblies schädlichen Code enthalten.

Allerdings lassen sich viele der geschilderten Probleme umgehen bzw. beheben.
Caching – dafür gibt es auch schon diverse, fertige APIs, die auch neben Caching noch weitere Verbesserungen für Reflection bringen.

Alle anderen Probleme brauchen eine vorsichtige und sparsame Verwendung von Reflection.
Mein Credo dabei ist: So viel wie nötig, so wenig wie möglich. Denn sehr vieles geht auch ohne Reflection, kann aber mit Reflection nachgebaut werden.

 

System.Linq.Expressions

Mit der Einführung von LINQ und in dessen Zuge auch dem neuen Namensraum System.Linq.Expressions gibt es eine weitere und einfachere Möglichkeit, Code dynamisch zu generieren und vorallem auch zu analysieren.

Zum einen ist es mit LINQ und den Expression Trees möglich, Lambda Ausdrücke, die in eine Methode eingespiesen werden, auseinander zu nehmen und zu analysieren.
In .NET selber wird das bspw. verwendet, um LINQ abfragen zu parsen und diese anschliessend in ein SQL-Statement zu überführen.

Zumindest laut meinem Wissen, ist es mit dem Expressions-API nur möglich, Methoden dynamisch zu generieren und zu kompilieren. Das ist eine Einschränkung, die allerdings für viele Anwendungsfälle nicht weiter tragisch ist, damit lässt einfach schon relativ viel realisieren.

Das Expressions-API nutzt intern auch Reflection und schlussendlich auch die Klassen aus dem Namensraum System.Reflection.Emit.

Beispiele mit dem Expressions-API:

Addieren von zwei Zahlen per statischem Code.
private void AddPlain()
{
  int one = 1;
  int two = 2;

  int result = one + two;
}

Addieren von zwei Zahlen, die Methode wird dynamisch zusammengebaut.
private static void AddWithExpressions()
{
  ConstantExpression numberOneExpression = Expression.Constant(2);
  ConstantExpression numberTwoExpression = Expression.Constant(3);

  BinaryExpression addExpression = Expression.Add(numberOneExpression, numberTwoExpression);

  var lambdaExpression = Expression.Lambda<Func<int>>(addExpression);
  Func<int> methodDelegate = lambdaExpression.Compile();

  int result = methodDelegate();
}


Funktion zum Addieren von zwei Zahlen per statischm Code.
private static int AddFunctionPlain(int left, int right)
{
  return left + right;
}

Funktion zum Addieren von zwei Zahlen dynamische generiert.
private static Func<int, int, int> GenerateAddFunction()
{
  ParameterExpression leftParameter = Expression.Parameter(typeof(int), "left");
  ParameterExpression rightParameter = Expression.Parameter(typeof(int), "right");

  BinaryExpression addExpression = Expression.Add(leftParameter, rightParameter);

  var lambdaExpression = Expression.Lambda<Func<int, int, int>>(addExpression, leftParameter, rightParameter);
  Func<int, int, int> methodDelegate = lambdaExpression.Compile();

  return methodDelegate;
}

Wie man sehen kann, ist die Nutzung von Expressions zum dynamische Generieren von Methoden für Ihre Mächtigkeit immer noch relativ einfach zu nutzen.

 

Fazit

Das war ein kurzer Rundflug durch Reflection und verwandte Gebiete, in dem ich versucht habe, die Vorteile, Möglichkeiten sowie auch Gefahren aufzuzeigen.
Reflection ist genial, aber nur wenn es sinngemäss und mit Vorsicht eingesetzt wird.

Also: Fluch und Segen :)

Wann IEnumerable nutzen, wann ICollection und wieso überhaupt IList?

Es gibt einige Mengen- /  Auflistungstypen in .NET.

Ein kleine Auswahl:

Früher gab es nur nicht generische bzw. keine stark typisierte Collections, die heute nicht mehr verwendet werden sollten,
da immer explizit gecastet werden muss, was umständlich ist und Performance kostet.

Hierfür zitiere ich gerne herbivore aus myCSharp:

ArrayList gehört in die Mottenkiste und sollte wie alle untypisierten Collections aus System.Collections nicht mehr benutzt werden. Verwende stattdessen List<T> und alle anderen typisierten Collections aus System.Collections.Generic.

Ich möchte hier allerdings nicht die verschiedenen Auflistungstypen im Detail anschauen, sondern herausfinden, wann welcher konkreter Typ oder gar das Interface in einer Signatur verwendet werden sollte.

Ein kleines Beispiel:


public void PrintNamesToConsole(List<string> names)
{
    foreach(string name in names)
    {
        Console.WriteLine(name);
    }
}

Dieser Code verwendet List<T> als Argumenttyp, es besteht also eine Abhängigkeit darauf,
d.h. jeder Aufrufer muss hier eine List<T> übergeben, alles andere funktioniert nicht.
Beispielsweise kann hier kein Array übergeben werden, oder eine Collection.

Wenn dann innerhalb der Methode, die das Argument entgegennimmt, nicht einmal
auf spezifische Methoden / Eigenschaften des konkreten Types (List<T>) zugegriffen wird,
ist das noch ein grösser Nachteil, denn man erkauft sich eine Abhängigkeit, ohne das man sie eigentlich benötigt.

Hört sich komisch an?
Eigentlich ist es relativ leicht, wenn wir uns mal die verschiedenen Schnittstellen betrachten, worauf die Auflistungen in .NET aufbauen.

Der eigentliche Kern ist die Schnittstelle IEnumerable bzw. der typisierte Bruder IEnumerable<T>, wobei dort T den Typ der enthaltenen Elemente angibt.

IEnumerable:

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

IEnumerable<T>:

public interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

Das Interface IEnumerator sieht dann so aus:
(Die generische Implementation IEnumerator<T>, hat zusätzlich eine Eigenschaft vom Typ T über die Eigenschaft Current).


public interface IEnumerator
{
    bool MoveNext();
    object Current { get; }
    void Reset();
}

Anhand der Beispiele ist zu sehen, das IEnumerable / IEnumerable<T> und die dazugehörige Schnittstelle für den
Enumerator beschreibt, das Elemente durchlaufen werden können. Nicht mehr und nicht weniger.

Das Beispiel von oben, könnte wie folgt umgeschrieben werden, um die kleinste Abhängigkeit zu haben und trotzdem
alle Anforderungen funktionieren, denn es muss ja nur eine Auflistung durchlaufen werden, nicht mehr und nicht weniger.

public void PrintNamesToConsole(IEnumerable<string> names)
{
    foreach(string name in names)
    {
        Console.WriteLine(name);
    }
}

Man beachte den geänderten Argumenttyp, der jetzt viel genereller ist.
Jetzt kann die Methode ein string[], eine List<T>, … entgegennehmen, einfach alles das die Schnittstelle IEnumerable<T> implementiert.

Wird eine Unterstützung in der Art von <Auflistung>.Count gefordert, kann die Schnittstelle ICollection<T> angegeben werden, die eine solche Eigenschaft nativ mitbringt.
Zusätzlich beschreibt das Interface (Erst ab der generischen Variante), das ein Element hinzugefügt, die Auflistung geleert, zurückgeben kann ob ein Element exisitert und Elemente entfernt werden können.

Durch LINQ to Objects können praktisch alle Operationen, wie bspw. Anzahl Elemente abfragen, sortieren, filtern, Zugriff über Index, damit und auf dem IEnumerable<T> Typen gemacht werden.
LINQ to Objects ist schnell, sehr schnell sogar. Wird jedoch die höchste Leistung benötigt und bspw. sehr häufig auf die .Count-Eigenschaft zugegriffen,
ist es besser, wenn eine native Unterstützung einer solchen Eigenschaft vorliegt.

LINQ to Objects arbeitet bei Count ungefähr so:

  • Versuche die Auflistung nach ICollection<T> zu casten.
  • Wenn das klappt, rufe die .Count-Eigenschaft ab und gib das Ergebnis zurück.
  • Wenn es nicht klappt, durchlaufe alle Einträge in der Auflistung, zähle die Anzahl zusammen und gib das Ergebnis zurück.

Das Interface IList<T> selber implementiert ICollection<T> – kann also alles auch – jedoch gibt es zusätzlich noch die Möglichkeit nativ per Index zu arbeiten. Also Elemente indexiert abfragen, ein Element an einem bestimmten Index löschen / einfügen.

In den meisten Fällen reicht es also, wenn IEnumerable<T> als Argumenttyp bzw. Rückgabetyp angegeben wird und in den anderen Fällen jeweils das Interface ICollection<T> / IList<T>, je nachdem was benötigt wird.

Es ist zu beachten, das IList<T> nicht alles beschreibt, was List<T> implementiert, bspw. gibt es dann keine Find- / FindAll-Methode, sowie auch keine native Implementierung für das Sortieren.
Mit LINQ to Objects tritt das allerdings in den Hintergrund und so kann man sich im Code von Abhängigkeiten zu den konkreten Auflistungstypen lösen.

Wieso soll man sich überhaupt lösen?

Umso genereller die Argument- oder Rückgabetypen sind, desto flexibler ist die API und es wird nicht mehr öffentlich gemacht, als schlussendlich verwendet wird.
Zusätzlich ist die Abhängigkeit zu einem konkreten Typ weg, was bedeutet das bspw. anstelle einer List<T> einfach irgend eine andere Implementierung von IEnumerable<T> empfangen / zurückgegeben werden kann.

So kann bspw. später eine Auflistung implementiert werden, die bei einem bestimmten Ereignis wie bspw. das Entfernen eines Eintrages, einen Event auslöst, ohne den Argumenttyp zu ändern.

Folgend noch ein paar sehr interessante Beiträge zum Thema:

Fehler verstecken leicht gemacht

Mit Try / Catch / Finally können in .NET Fehler behandelt werden.
Eigentlich eine gute Sache, allerdings sollte man aufpassen wo und wie man Fehler behandelt.

Vielerorts kann gelesen werden, dass das Fangen einer Allgemeinen Exception nicht gut sei, aber wieso ist das so?

Da ich es auch schon selber praktisch mehrmals erlebt habe, was das für schlimme Auswirkungen haben kann, ist es für mich nicht so schwer, diese Frage zu beantworten.

Ich möchte dies anhand eines kleinen, nachvollziehbaren Beispiels erläutern.

Gegeben ist folgender Code:


protected void Page_Load(object sender, EventArgs e)
{
    try
    {
        this.GridView1.DataSource = <DataSource>;
        this.GridView1.RowDataBound += GridView1_RowDataBound;
        CommandField deleteField = new CommandField
                   {
                     ShowDeleteButton = !this._recordsAreReadOnly,
                     DeleteText = “Löschen”,
                     DeleteImageUrl = "delete.png",
                     ButtonType = ButtonType.Image
                   };

         GridView1.Columns.Add(deleteField);
        // zusätzlichen Code…
        this.GridView.DataBind();
    }
    catch(Exception ex)
    {
        this.Logger.Log(ex);
    }
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if(e.Row.RowType == DataControlRowType.DataRow)
    {
        // zusätzlicher Code…
        ImageButton deleteButton = e.Row.FindControlRecursive<ImageButton>(c => c.CommandName == “Delete”);
        deleteButton.CssClass = “button_delete”;
        // zusätzlicher Code…
    }
}

Dieser Code, läuft - wie er jetzt da steht - ohne Probleme, solange die Daten nie als readonly gekennzeichnet werden.
Wird jetzt allerdings der Code erweitert, gibt es ein höchst merkwürdiges Verhalten, das ich mir zuerst überhaupt nicht erkären konnte.

Folgende Änderung:


ShowDeleteButton = !this._recordsAreReadOnly,

wird zu:


ShowDeleteButton = !this._recordsAreReadOnly && <Sicherheitsabfrage ob gelöscht werden darf>,

Der erste Teil der Bedingung liefert mit der Negation schlussendlich true und die Sicherheitsabfrage false, was ausgewertet dann einem false entspricht.
Somit werden die Löschen-Buttons nicht dargestellt. Gut. Eigentlich genau das was ich wollte.

Jedoch – und jetzt kommt der Haken – wird jetzt nur noch ein Datensatz im GridView angezeigt, anstelle von den zwei die in der Datenquelle vorhanden sind.

Im ersten Moment kam mir das ziemlich merkwürdig vor, da ich auch nirgendwo nochmals auf die Bedingung zugreifen und vor allem, weil auch kein Fehler geworfen wurde.

Kurz nachdem ich den Debugger angeworfen und kurz in den EventHandler “GridView1_RowDataBound” reingeschaut habe, kam es mir in den Sinn:

Böses Try / Catch, ein Fehler wurde versteckt, ohne das ich es gemerkt habe.

Was ist denn genau passiert?

Nun, da über die komplette Page_Load-Methode ein Try / Catch Konstrukt gespannt ist, das generell alle Fehler abfängt (Nicht behandelt, sondern eben “verschluckt”), bekomme ich den Fehler nicht zu Gesicht und der Code läuft in einem inkonsistenten Zustand weiter.

Der Eventhandler wird angemeldet und ein GridView1.DataBind()-Aufruf läuft im Eventhandler selber, also auch im Scope von besagtem Try / Catch, sodass die NullReferenceException, die eigentlich geworfen werden sollte, verschluckt wird.

Das hat dann dazu geführt, dass das GridView “irgendwie” noch halb fertig gerendert wird und man nichts vom Fehler mitbekommt, sondern eben nur einen Datensatz halb fertig gerendert wird.

Ich hoffe das dieses Beispiel ein wenig Klarheit bringt und vor allem euch aufweckt, Exception Handling mit Bedacht einzusetzen und nicht an einem solch komischen Verhalten zu verzweifeln.

Zusatz / Fazit:
Wie man sehen kann, ist der Scope (Wirkungsbereich) von Try / Catch um die ganze Page_Load-Methode gelegt.
Dies kann Sinn machen, jedoch nicht in der Mehrzahl der Fälle, wie auch in diesem Fall.

Meiner Meinung nach, und so steht es auch in den meisten Büchern, sollten Fehler punktuell abgefangen werden, nur dort wo man ihn auch behandeln kann und der Scope sollte möglichst klein gehalten werden, damit das Problem auch schnell identifiziert ist.

Die unbehandelten Fehler kann man dann generell in ASP.NET bspw. in der Global.asax.cs in der Methode Application_Error() loggen.
Ich persönlich gehe sogar so vor, das ich zuerst ohne Fehlerbehandlung entwickle und diese erst später hinzufüge.
So gehen während der Entwicklung keine Fehler vergessen und das Programm befindet sich nie in einem inkonsistenten Zustand.

Solchen Code wie oben gezeigt ist - wie auch Gregor im Kommentar bemerkt hat - vielerorts zu finden, was ich tragisch finde.
Die Konsequenzen daraus müssen in keinem Fall so harmlos sein, wie in diesem Beispiel gezeigt. Man stelle sich nur mal ein Atomkraftwerk vor, das durch eine inkonsistente Software gesteuert wird.

Bearbeitung / Korrekturen:
13.11.09 - Fehlendes Komma in den Codesnippets hinzugefügt
13.11.09 - Zusatz / Fazit hinzugefügt

Eingetragen von Peter Bucher | 7 Kommentare