-
ACHTUNG - NEUES BLOG
Ab sofort steht unter http://devtyr.norberteder.com mein neues Blog zur Verfügung. Dieses Blog wird nicht weiter betreut, bleibt aber erhalten. Neue Eintr%auml;ge erfolgen nur mehr im neuen Blog. Kommentare werden ebenfalls nicht mehr behandelt. Wer weiterhin meinen Einträgen und Aktivitäten folgen möchte, möge bitte RSS-Feeds, Verlinkungen etc. an die neue Location anpassen.
|
C# 3.0: Anonyme Typen
28.09.07 - .NET, Grundlagen, Base Framework, Allerlei Beitrag von Norbert Eder| | Nachdem ich bereits über Anonymen Delegates (auch unter .NET 2.0 verfügbar) als auch dem unter C# 3.0 neuen Keyword var geschrieben habe, möchte ich ein wenig über Anonyme Typen schreiben.
Anonyme Typen sind ähnlich zu anonymen Methoden (dazu kommen wir dann auch in einem der nächsten Blogbeiträge).Dies bedeutet, dass anonyme Typen inline gebildet werden können. Um dies zu verdeutlichen gleich ein kleines Beispiel:
Wie zu sehen ist, ist hierfür das Schlüsselwort var zu verwenden. Die Definition erfolgt inline und definiert welche Eigenschaften der neue Typ hat. Selbst IntelliSense kennt die neuen Eigenschaften.
Anonyme Typen kennzeichnen sich dadurch aus, dass sie keinen zugewiesenen Namen besitzen (dies ist auch in der Typen-Beschreibung - siehe IntelliSense - ersichtlich).
Werden im gleichen Scope zwei anonyme Typen definiert, welche die gleiche Signatur besitzen, kann der erste Type den zweiten einfach übernehmen, wie nachfolgend zu sehen:
Wozu werden anonyme Typen nun gebraucht?
Zum einen wurden anonyme Typen natürlich für LINQ eingeführt. Diese werden dort häufig verwendet und bilden daher eine Notwendigkeit. Aber auch für andere Zwecke können sie verwendet werden.
Grundsätzlich bieten sich anonyme Type dann an, wenn ein Typ kurzfristig (für die Ausgabe etc.) benötigt wird, aber nicht für die gesamte Anwendung zur Verfügung stehen muss. In diesem Fall muss also keine eigene Klasse erstellt werden, welche schlussendlich nur an einer einzigen Stelle verwendet werden würde. Wann anonyme Typen einzusenden sind bleibt jedem Entwickler selbst überlassen. Unter dem obigen Gesichtspunkt sollte aber durchaus klar sein, wann eine Verwendung sinnvoll ist und wann weniger.
| | | Kommentar hinzufügen
| 0 Trackbacks
| Permalink | Trackback-URL |
Anonymous Delegates
25.09.07 - .NET, Grundlagen, Base Framework Beitrag von Norbert Eder| | Dem einen oder anderen mögen anonyme Methoden (oder beispielsweise anonyme Typen wie sie mit C# 3.0 kommen) bekannt sein. In diesem Beitrag möchte ich ein wenig über anonyme Delegates schreiben und wozu diese benötigt werden (sind ohnehin sehr ähnlich zu anonymen Methoden).
Zuerst gleich das Beispiel, anhand dessen die Erklärungen vorgenommen werden:
using System;
using System.Collections.Generic;
using System.Text;
namespace AnonymousDelegatesSample
{
public class Book
{
public string Title;
public decimal Price;
}
public class Person
{
public string Firstname;
public string Lastname;
public List<Book> BookList;
public decimal Discount;
public DiscountCalculator CalculationMethod;
}
public delegate decimal DiscountCalculator(decimal price);
public class Example
{
private decimal CalculateDiscount(decimal price)
{
return price * new decimal(0.05);
}
public void ShowExample()
{
DiscountCalculator default_calc =
new DiscountCalculator(CalculateDiscount);
DiscountCalculator specialcustomer_calc =
delegate(decimal price)
{
return price * new decimal(0.09);
};
Book book1 = new Book();
book1.Title = "ASP.NET for dummies";
book1.Price = new decimal(35.99);
Book Book2 = new Book();
Book2.Title = "C# for dummies";
Book2.Price = new decimal(49.99);
List<Book> bookList = new List<Book>();
bookList.Add(book1);
bookList.Add(Book2);
List<Person> personList = new List<Person>();
Person person1 = new Person();
person1.Firstname = "Norbert";
person1.Lastname = "Eder";
person1.BookList = bookList;
person1.CalculationMethod = specialcustomer_calc;
personList.Add(person1);
Person person2 = new Person();
person2.Firstname = "Alf";
person2.Lastname = "Dudldei";
person2.BookList = bookList;
person2.CalculationMethod = default_calc;
personList.Add(person2);
foreach (Person p in personList)
{
decimal price = 0;
foreach (Book b in p.BookList)
price += b.Price;
p.Discount = p.CalculationMethod(price);
Console.WriteLine(
String.Format("{0} {1}:t {2} discount",
p.Firstname,
p.Lastname,
p.Discount.ToString()));
}
Console.WriteLine("Press any key ...");
Console.ReadKey();
}
}
}
Zuerst ein kurze Erklärung wozu dieses Beispiel dient: Grundsätzlich gibt es zwei Klassen. Zum einen die Person und zum anderen das Book. Wir gehen also davon aus, dass eine Person Bücher kauft und diese in einer Collection aufbewahrt werden. Nun soll (je nach Kundenstatus) ein Rabatt berechnet werden.
Zur Erklärung: Dies kann natürlich auch auf eine andere - vermutlich einfachere Art und Weise implementiert werden. Dieses Beispiel dient lediglich der Veranschaulichung von anonymen Delegates.
Grundsätzlich ist in diesem Beispiel ein üblicher Delegate definiert:
public delegate decimal DiscountCalculator(decimal price);
Dieser wird im Standardfall auch verwendet, um eine Methode aufzurufen, die für die Rabattberechnung zuständig ist.
DiscountCalculator default_calc =
new DiscountCalculator(CalculateDiscount);
Die aufgerufene Methode sieht dann so aus:
DiscountCalculator default_calc =
new DiscountCalculatprivate decimal CalculateDiscount(decimal price)
{
return price * new decimal(0.05);
}or(CalculateDiscount);
Nun kann es jedoch vorkommen, dass eine spezielle Berechnung durchgeführt werden muss, diese wird nur in einem ganz bestimmten Fall angewandt und bedarf daher keiner eigenen Methoden-Implementierung (in diesem Beispiel vielleicht weniger sinnvoll, aber generell treten bei der Verwendung von Delegates immer wieder Fälle auf, in denen sich eine eigene Methode nicht rentiert). Hier kommen nun anonyme Delegates ins Spiel. Die aufgerufene Methode ist hierbei ansich nicht vorhanden (daher anonym) und wird erst bei der Erstellung des Delegate definiert. Wie hier:
DiscountCalculator specialcustomer_calc =
delegate(decimal price)
{
return price * new decimal(0.09);
};
Damit wird im Grunde erreicht, dass die eigentliche Berechnung durch eine neue ersetzt wird. Eine entsprechende Methode muss hierzu nicht vorhanden sein.
Einsatzgebiete hierfür finden sich immer wieder, daher grundsätzlich nicht schlecht, wenn man sie kennt und auch anwenden kann. Im Falle des Falles muss der Entwickler ohnehin selbst entscheiden ob dies sinnvoll ist oder nicht. Wird die Berechnung anderweitig verwendet ist es sicherlich sinnvoll, diese in eine eigene Methode zu kapseln. Andernfalls kann durchaus ein anonymer Delegate verwendet werden.
| | | Kommentar hinzufügen
| 0 Trackbacks
| Permalink | Trackback-URL |
C# 3.0: Keyword var
18.09.07 - .NET, Grundlagen, Base Framework Beitrag von Norbert Eder| | Nein, var steht hier nicht in nostalgischer Verbindung zu Visual Basic. Vielmehr handelt es sich – streng genommen – auch nicht wirklich um ein Schlüsselwort (Keyword). Schlüsselwörter dürfen ja nicht in Klassen, Strukturen etc. verwendet werden, var hingegen sehr wohl. Aber unabhängig dieser Tatsache, was verbirgt sich dahinter?
Beginnen wir mit einem kleinen Beispiel:
var htTemp = new Hashtable();
In diesem Fall versucht der Compiler auf den Typ von strList zu schließen, was auch kein Problem darstellt, da dieser ohnehin durch new Hashtable() definiert wird. Es entfällt also die lange und natürlich auch unschöne und redudante Darstellung á la:
Hashtable htTemp = new Hashtable();
Manche mögen nun der Meinung sein, dass sich C# dadurch von der starken Typisierung verabschiedet. Dem ist jedoch nicht so. Wurde die Variable deklariert, wird auf ihren Typ geschlossen und zugewiesen. Der Typ kann an einer späteren Stelle nicht mehr verändert werden (wie dies beispielsweise unter VB.NET möglich ist).
Ebenfalls sei zu betonen, dass var auch nur bei der Initialisierung verwendet werden kann.
| | | 5 Kommentare
- 618 mal angesehen
| 0 Trackbacks
| Permalink | Trackback-URL |
Visual Studio One: Geht es etwas schneller?
12.09.07 - .NET, Base Framework, Allerlei Beitrag von Norbert Eder| | In der aktuellen Ausgabe der Visual Studio One ist wieder ein Artikel von mir zu finden.
Verbesserte Prozessoren, RAM wie niemals zuvor. Dennoch kann nichts schnell genug gehen. Doch was, wenn Teile der Software den Kundenwünschen hinsichtlich Geschwindigkeit nicht entsprechen? Dieser Artikel führt in das Thema Performance-Verbesserung ein und zeigt Möglichkeiten auf, Ihre Software auf Trab zu bringen.
| | | Kommentar hinzufügen
| 0 Trackbacks
| Permalink | Trackback-URL |
C#: Eintrag in das EventLog hinzufügen
04.08.07 - .NET, Grundlagen, Base Framework Beitrag von Norbert Eder| | Das EventLog stellt eine gute Basis dar, Informationen, oder aufgetretene Probleme abzulegen. Hier ein kleines Beispiel, wie dies funktioniert:
if (!EventLog.SourceExists("TestCategory"))
EventLog.CreateEventSource("TestCategory", "TestLog");
EventLog evtLog = new EventLog();
evtLog.Source = "TestCategory";
evtLog.WriteEntry("This is a test", EventLogEntryType.Information);
Zuerst wird lediglich überprüft, ob eine bestimmte Kategorie im EventLog bereits vorhanden ist. Wenn nicht erfolgt eine Anlage.
Anschließend wird ein Objekt vom Typ EventLog erstellt, als Source die soeben angelegte Kategorie zugewiesen. Mittels WriteEntry kann nun ein Eintrag erstellt werden. EventLogEntryType legt den Typ des zu schreibenden Eintrages fest.
| | | 2 Kommentare
- 797 mal angesehen
| 0 Trackbacks
| Permalink | Trackback-URL |
.NET: Directory.Move und unterschiedliche Volumes
02.08.07 - .NET, Grundlagen, Base Framework Beitrag von Norbert Eder| | Mit Directory.Move ist es sehr einfach, komplette Verzeichnisse zu verschieben. Ein Problem stellt sich lediglich dann ein, wenn Verzeichnisse zwischen unterschiedlichen Volumes verschoben werden sollen. Dies wird durch Directory.Move nicht abgedeckt. Stattdessen muss auf eine eigene Implementierung zurück gegriffen werden.
Mit Hilfe einer Rekursion kann das dann in etwa so aussehen:
public void MoveDirectory(
string strSourceDir,
string strDestDir,
bool bDelSource)
{
if (Directory.Exists(strSourceDir))
{
if (Directory.GetDirectoryRoot(strSourceDir) ==
Directory.GetDirectoryRoot(strDestDir))
{
Directory.Move(strSourceDir, strDestDir);
}
else
{
try
{
CopyDirectory(new DirectoryInfo(strSourceDir),
new DirectoryInfo(strDestDir));
if (bDelSource)
Directory.Delete(strSourceDir, true);
}
catch (Exception subEx)
{
throw subEx;
}
}
}
}
private void CopyDirectory(
DirectoryInfo diSourceDir,
DirectoryInfo diDestDir)
{
if (!diDestDir.Exists)
diDestDir.Create();
FileInfo[] fiSrcFiles = diSourceDir.GetFiles();
foreach (FileInfo fiSrcFile in fiSrcFiles)
fiSrcFile.CopyTo(Path.Combine(diDestDir.FullName, fiSrcFile.Name));
DirectoryInfo[] diSrcDirectories = diSourceDir.GetDirectories();
foreach (DirectoryInfo diSrcDirectory in diSrcDirectories)
CopyDirectory(
diSrcDirectory,
new DirectoryInfo(
Path.Combine(diDestDir.FullName, diSrcDirectory.Name)));
}
Was ich an Directory.Move allerdings nicht ganz verstehe ist, dass eben Verzeichnisse nicht auf unterschiedliche Volumes verschoben werden können. File.Move kann dies, warum also Directory.Move nicht. Wenn also jemand einen Ansatz hat, warum das so sein könnte, bitte melden, würde mich wirklich interessieren.
| | | 5 Kommentare
- 730 mal angesehen
| 0 Trackbacks
| Permalink | Trackback-URL |
C#: Entwicklung einer Notiz-Anwendung anhand eines Beispiels
19.07.07 - .NET, Grundlagen, Base Framework, WPF, Datenverwaltung, Visual Studio, Microsoft Office Beitrag von Norbert Eder| | Nachdem ich heute zufällig darüber gestolpert bin, möchte ich diese Artikel-Serie natürlich nicht vorenthalten. Worum geht es konkret:
StickOut ist eine Desktopanwendung für Kurznotizen mit Unterstützung für mehrere Benutzer und Integration in Outlook. Es handelt sich um eine Windows Forms-Anwendung für .NET Framework 2.0, bei der die Kommunikation mit anderen StickOut-Benutzern sowie der Austausch von Kurznotizen über .NET Remoting stattfindet. Der neue IPC-Kanal von .NET Framework 2.0 wird zur Kommunikation zwischen Microsoft Outlook und dem StickOut-Prozess verwendet. Mit dem Outlook-Add-In können Sie von beliebigen Outlook-Objekten Kurznotizen auf dem Desktop erstellen, einschließlich E-Mails, Notizen, Aufgaben, Terminen usw. Es wurde darauf geachtet, den Speicherbedarf einer .NET-Anwendungen maßgeblich zu verringern und einen zügigen und reibungslosen Umgang für den Benutzer zu gewährleisten. Dieser mehrteilige Artikel enthält einen Bericht über die Entstehung, beginnend mit meinem ersten Tag der Anwendungsplanung, hin zu allen nachfolgenden Entwurfs-, Entwicklungs-, Test- und Bereitstellungsphasen. Er zeigt die Fortentwicklung der Anwendung bis hin zu den Komplikationen, die bei den einzelnen Schritten aufgetreten sind. Sie erfahren vieles über .NET-Tricks, Bereitstellungs- und Versionskontrollprobleme, Visual Studio-Tricks und weitere nicht direkt auf die Entwicklung bezogene Tricks, die für Ihre tägliche Entwicklungsarbeit nützlich sein können.
Wer daran Interesse hat, hier die Links zu den einzelnen Teilen dieser Serie:
StickOut: Entstehung einer Kurznotiz-Anwendung in .NET 2.0, Teil 1
StickOut: Entstehung einer Kurznotiz-Anwendung in .NET 2.0, Teil 2
StickOut: Entstehung einer Kurznotiz-Anwendung in .NET 2.0, Teil 3
| | | 10 Kommentare
- 1359 mal angesehen
| 0 Trackbacks
| Permalink | Trackback-URL |
C#: Schnell und generisch Objekte erstellen
19.07.07 - .NET, Grundlagen, Base Framework, Allerlei Beitrag von Norbert Eder| | Oft müssen Objekte dynamisch bzw. generisch erzeugt werden. In den meisten Fällen wird zu Activator.CreateInstance oder Activator.CreateInstance<> gegriffen. Die Erzeugung über den Activator ist jedoch nicht die schnellste. Einen wesentlich performantereren Ansatz liefern uns die FormatterServices. Hier eine Objekt-Erzeugungs-Klasse, welche beide Varianten implementiert:
public class ObjectGenerator
{
public T GenerateObject<T>()
{
return (T)FormatterServices.GetUninitializedObject(typeof(T));
}
public T ActivateObject<T>()
{
return (T)Activator.CreateInstance<T>();
}
}
Erzeugen wir und für jede Variante in einer Schleife 10.000 Objekte, sehen wir eine kleine Überraschung. Zuerst der Code:
ObjectGenerator og = new ObjectGenerator();
Stopwatch sw = new Stopwatch();
Console.Write("Generate Object [Formatter]: ");
sw.Start();
for (int i = 0; i < 10000; i++)
{
TestClass tc = og.GenerateObject<TestClass>();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
Console.Write("Generate Object [Activator]: ");
sw.Start();
for (int i = 0; i < 10000; i++)
{
TestClass tc = og.ActivateObject<TestClass>();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Console.ReadKey();
Nun die Überraschung:
Generate Object [Formatter]: 3
Generate Object [Activator]: 21
Und jetzt kommt der eigentliche Clou: Mit Hilfe von FormatterServices.GetUninitializedObject ist es möglich Objekte von Klassen mit privaten Konstruktoren (kein öffentlicher Konstruktor vorhanden!!) zu erstellen. Hier eine Beispielklasse:
public class TestClass
{
private TestClass() { }
public string GetMessage()
{
return "This is a message";
}
}
Diese kann mit GetUninitializedObject instantiiert werden, wogegen CreateInstance eine MissingMethodException wirft und uns darauf aufmerksam macht:
Für dieses Objekt wurde kein parameterloser Konstruktor definiert.
Einsatzmöglichkeiten dafür werden sich sicherlich für manche finden.
| | | 6 Kommentare
- 4584 mal angesehen
| 0 Trackbacks
| Permalink | Trackback-URL |
.NET BlogBook Ausgabe 3 veröffentlicht
14.07.07 - .NET, Grundlagen, Base Framework, ASP.NET, Visual Studio, Allerlei, SQL Server, Internet, Community Beitrag von Norbert Eder| | Soeben wurde das .NET BlogBook in der Version 3 (oder auch Ausgabe genannt) veröffentlicht. In dieser Ausgabe wurden zahlreiche Unstimmigkeiten und Fehler bereinigt, als auch die Lesbarkeit weiter erhöht. Durch zahlreiche Rückmeldungen von Lesern konnten wir sie weiter an die Bedürfnisse der Leser anpassen.
Wir hoffen auch dieses Mal auf eine positive Resonanz und würden uns über konstruktive Kritik und Meinungen freuen.
Zu finden gibt es das Buch auf der .NET BlogBook Projektseite.
| | | 4 Kommentare
- 1014 mal angesehen
| 1 Trackbacks
| Permalink | Trackback-URL |
C#: BinaryFormatter, SerializationException und dynamisch geladene Assemblies
12.07.07 - .NET, Grundlagen, Base Framework Beitrag von Norbert Eder| | Zwei Solutions. In der ersten Solution befindet sich eine Windows Forms Anwendung. Diese referenziert eine Assembly aus der zweiten Solution. In der zweiten Solution befindet sich eine Konsolen-Anwendung, welche eben erwähnte Assembly per Assembly.GetAssembly lädt. Zu erwähnen sei ebenfalls, dass folgende Klassen enthalten sind:
- Datenklassen (diese werden serialisiert)
- Commands (siehe Command Pattern)
Nun gut. In der Windows Forms Anwendung werden die einzelnen Commands konfiguriert, und per BinaryFormatter serialisiert. Dabei ergibt sich jedoch ein kleines Problem:
Da die Konfigurationen in der Windows Forms Anwendung serialisiert wurden (hierzu muss angemerkt werden, dass sich die Klasse, welche die Konfigurationen liest und auch wieder schreibt nicht in der besagten Assembly befindet) und in der zweiten Solution (mit der Konsolen-Anwendung) dynamisch geladen werden sollte, wurde eine SerializationException geworfen. Beim Binary-Formatter wird unter anderem die genaue Assembly-Bezeichnung, Versionsnummer etc. hinterlegt, die zwar in diesem Fall zusammenpassen würde, aber dennoch konnten die entsprechenden Typen nicht gefunden werden (Referenz war gesetzt, Assembly wurde in die aktuelle AppDomain geladen).
Ein Ausweg ist hierbei das Setzen der Eigenschaft Binder des BinaryFormatters. Durch eine Ableitung der Klasse SerializationBinder kann das Verhalten, wie Typen geladen bzw. gefunden werden, selbst beeinflusst werden. In meinem Fall habe ich folgendes fabriziert, was dann auch wunderbar funktionierte:
public class Binder : SerializationBinder
{
public override Type
BindToType(string assemblyName, string typeName)
{
Type type = null;
string shortAssem = assemblyName.Split(',')[0];
Assembly[] assemblies =
AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assem in assemblies)
{
if (shortAssem == assem.FullName.Split(',')[0])
{
type = assem.GetType(typeName);
break;
}
}
return type;
}
}
Wer weiterführende Informationen diesbezüglich benötigt wird entweder via Google fündig, oder stellt seine Frage einfach als Kommentar. Ich versuche diese dann zu beantworten.
| | | Kommentar hinzufügen
| 0 Trackbacks
| Permalink | Trackback-URL | Zurück Weiter
|
|
|
|
|
|
|