blueberry
Goto Top

C Sharp - Extensions

In dieser kleinen Anleitung, möchte ich zeigen, was Extensions sind und wie man sie unter C# einsetzt.
Beiläufig wird es noch Typen-Konvertierungen beinhalten, damit das auch mal geklärt ist.

back-to-topExtension Method - Was ist das?
Mittels einer extension Method kann man einen Datentyp erweitern, d.h. wenn ich eine extension Method für den Datentyp String definiere, kann man darauf zugreifen, als ob man auf eine andere schon vorhandene bzw. schon in der String-Class implementierte Methode zugreift.

back-to-topBedingungen einer extension Method
Um eine extension Method zu schreiben, muss man lediglich ein paar Kleinigkeiten beachten:
    • Die Klasse in der die Methode geschrieben wird, muss statisch sein
    • Die Methode selbst muss auch statisch sein
    • Der erste Parameter muss vom entsprechenden Datentype sein und muss mit this versehen sein (siehe unter Implementierung)
    • Man braucht dazu mind. das .NET-Framework 3.5 (oder höher selbstverständlich)

back-to-topImplementierung
Hier ein paar Beispiele

Unter dem Code gibt es noch eine Erklärung
using System;
using System.IO;
using System.Text;

namespace Basics
{
    public static class Converter
    {
        /// <summary>
        /// Konvertiert einen String in einen Int32.
        /// </summary>
        /// <param name="str">Input-Parameter</param> 
        /// <returns>In Int32 konvertierter String</returns>
        /// <exception cref="FormatException">Tritt auf, wenn der String nicht erlaubte zeichen beinhaltet</exception> 
        /// <exception cref="OverflowException">Tritt auf, wenn die Zahl zu gross ist</exception> 
        public static int ToInt32(this String str)
        {
            return Convert.ToInt32(str);
        }

        /// <summary>
        /// Konvertiert einen String in einen Int32.
        /// Bei einer Exception wird die Default-Value zurückgegeben.
        /// </summary>
        /// <param name="str">Input-Parameter</param> 
        /// <param name="defValue">Default-Value</param> 
        /// <returns>In Int32 konvertierter String</returns>
        public static int ToInt32(this String str, Int32 defValue)
        {
            try
            {
                return ToInt32(str);
            }
            catch (Exception)
            {
                return defValue;
            }
        }

        /// <summary>
        /// Konvertiert einen String in einen bool.
        /// </summary>
        /// <param name="str">Input-Parameter</param> 
        /// <returns>Converted String bool</returns>
        public static bool ToBool(this String str)
        {
            switch(str.ToLower())
            {
                case "1":  
                case "true":  
                case "yes":  
                    return true;
                case "0":  
                case "false":  
                case "no":  
                    return false;
                default:
                    throw new FormatException("Kann nicht konvertiert werden.");  
            }
        }
    }
}
Hier sieht man den gesamten Aufbau, die statische Klasse, die statische Methoden und auch die mit dem this versehenen Parameter. Man sieht in der ToInt32-Überladung, dass man auch auf extension Methoden in einer Extension zugreifen kann. Dass ermöglicht einem, dass man auch bei extension Methoden nichts zwei Mal implementieren muss. Die ToBool-Methode finde ich vorallem sehr praktisch, da man diese ganz einfach erweitern kann. Man könnte z.B. auch eine CSV exportieren und dort diese für den Benutzer bereitstellen, so kann er selbst entscheiden, was true oder false ergeben soll.

Man kann auch Generics verwenden und die Extensions sind nicht nur auf die Basis-Typen anwendbar:

/ <summary>
/ Konvertiert ein byte-Array in einen string
/ </summary>
/ <param name="arr">Input-Parameter</param>
/ <returns>String welcher aus dem Byte-Array konvertiert wurde</returns>
public static string GetString<T>(this byte arr) where T: Encoding, new()
{
return new T().GetString(arr);
}

/ <summary>
/ Liest einen Stream aus und gibt die Daten zurück.
/ Seek-Unabhängige Methode.
/ </summary>
/ <param name="stream"></param>
/ <param name="initSize"></param>
/ <returns></returns>
public static byte GetData(this Stream stream, int initSize)
{
if (stream == null)
throw new ArgumentNullException("stream");

Überprüfen, ob man den Stream lesen kann.
if (!stream.CanRead)
throw new ArgumentException("Stream kann nicht gelesen werden!");

if (initSize < 1)
initSize = 128;

byte buffer = new byte[initSize];
int currByte, count = 0;

while((currByte = stream.ReadByte()) != -1)
{
buffer[count] = (byte)currByte;
count++;

if(count == buffer.Length - 1)
{
byte tmpBuffer = buffer;
buffer = new byte[tmpBuffer.Length * 2];
tmpBuffer.CopyTo(buffer, 0);
}
}

Array.Resize(ref buffer, count);

return buffer;
}

/ <summary>
/ Konvertiert die Daten eines Streams in einen string mit dem angegebenen Encoding.
/ </summary>
/ <param name="stream">Input Stream</param>
/ <returns>Gibt den stream in dem gewünschten Format zurück</returns>
public static string GetStringData<T>(this Stream stream) where T : Encoding, new()
{
return stream.GetData().GetString<T>();
}

Mit solchen Extensions könnt ihr gut konvertierungen machen und müsst nicht mehr gross an die Typen-Konvertierung denken, wenn ihr am Arbeiten seid. Natürlich könnt ihr auch andere Funktionalitäten, welche nicht unbedingt eine Type-Konvertierung brauchen mittels Extensions implementieren, allerdings habe ich dafür noch nicht wirklich anwendung gefunden...

Edit: Habe jetzt auch schon eine Extension, welche nicht unbedingt eine Typen-Konvertierung beinhaltet.
Es geht dabei um eine Extension, welche tatsächlich neue Funktionalität zur Verfügung stellt. Die unten gezeigte Extension greift auf die User32.dll zu und ändert den Status der ProgressBar, damit man trotz aktiven Visual-Styles eine rote ProgressBar hinbekommt, ich konnte diese Methoden schon öfters gebrauchen.

        #region Progressbar

        #region Enum & Constants
        public enum ProgressBarState : int
        {
            NORMAL = 1,
            ERROR = 2,
            PAUSED = 3,
        }

        public const int WM_USER = 0x400;
        public const int PBM_SETSTATE = WM_USER + 16;
        public const int PBM_GETSTATE = WM_USER + 17;

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]  
        static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        
        #endregion

        #region Methods

        public static ProgressBarState GetState(this ProgressBar pBar)
        {
            return (ProgressBarState)(int)SendMessage(pBar.Handle, PBM_GETSTATE, IntPtr.Zero, IntPtr.Zero);
        }

        public static void SetState(this ProgressBar pBar, ProgressBarState state)
        {
            SendMessage(pBar.Handle, PBM_SETSTATE, (IntPtr)state, IntPtr.Zero);
            //Die folgende Zeile sorgt dafür, dass alle Messages (z.B. die von oben) verarbeitet werden und erst dann weiter gemacht wird.
            Application.DoEvents();
        }

        #endregion

        #endregion

Ich hoffe das Tut konnte euch etwas veranschaulichen, was eine Extension ist und wie man sie implementiert und anwendet.
Über Feedback und Kritik freue ich mich immer, allerdings ist es mein erstes Tut welches ich schreibe, seid also nicht zu hart zu mir face-wink.
Wenn ihr Verbesserungsvorschläge habt, dann sagt mir unbedingt bescheid!

Edit: Stream-Extension 'GetData' Seek-Unabhängig implementiert.

Content-ID: 180122

Url: https://administrator.de/tutorial/c-sharp-extensions-180122.html

Ausgedruckt am: 23.12.2024 um 05:12 Uhr

Herbrich19
Herbrich19 20.02.2012, aktualisiert am 21.02.2012 um 00:05:48 Uhr
Goto Top
Kann ich zwar in Moment nicht wirklich gebrauchen, aber sehr schön übersichtlich. Ich werde unbedingt mal eine kleine Test-App schreiben und es mal testen, aber so kann man sich wirklich die Arbeit an den Konvertierungen sehr stark vereinfachen.

Grüße, Herbrich17
Blueberry
Blueberry 22.02.2012 um 16:13:34 Uhr
Goto Top
Hallo Herbrich17

Vielen Dank für dein Feedback!
Ich habe mittlerweilen auch eine Extension geschrieben, welche nicht nur für Typen-Konvertierung geeignet ist, ich habe (bzw. werde) diese oben im Beitrag noch erweitern zum zeigen, dass man die Extensions auch für andere Sachen gebrauchen kann.

Gruss Blueberry