Sök…


Syntax

  • public static ReturnType MyExtensionMethod (detta TargetType-mål)
  • public static ReturnType MyExtensionMethod (detta TargetType-mål, TArg1 arg1, ...)

parametrar

Parameter detaljer
detta Den första parametern i en förlängningsmetod bör alltid föregås av det this nyckelordet, följt av den identifierare som man vill hänvisa till den "aktuella" instansen för objektet du utökar

Anmärkningar

Förlängningsmetoder är syntaktiskt socker som gör att statiska metoder kan åberopas i föremålstillfällen som om de var en medlem av själva typen.

Förlängningsmetoder kräver ett tydligt målobjekt. Du måste använda det this nyckelordet för att komma åt metoden från själva den utökade typen.

Tilläggsmetoder måste förklaras statiska och måste leva i en statisk klass.

Vilket namnutrymme?

Valet av namnutrymme för din klass för tilläggsmetoder är en avvägning mellan synlighet och upptäckbarhet.

Det vanligaste alternativet är att ha ett anpassat namnutrymme för dina förlängningsmetoder. Men detta kommer att innebära en kommunikationsinsats så att användare av din kod vet att förlängningsmetoderna finns och var de kan hitta dem.

Ett alternativ är att välja ett namnområde så att utvecklare kommer att upptäcka dina förlängningsmetoder via Intellisense. Så om du vill utöka Foo klassen är det logiskt att sätta tilläggsmetoderna i samma namnområde som Foo .

Det är viktigt att inse att ingenting hindrar dig att använda "någon annans" namnutrymme : Om du vill utöka IEnumerable kan du lägga till din förlängningsmetod i System.Linq namnområdet.

Det här är inte alltid en bra idé. I ett specifikt fall kan du till exempel vill utöka en vanlig typ ( bool IsApproxEqualTo(this double value, double other) till exempel), men har inte det som "förorenar" hela System . I detta fall är det att föredra att välja ett lokalt, specifikt namnutrymme.

Slutligen är det också möjligt att placera förlängningsmetoderna i inget namnområde alls !

En bra referensfråga: Hur hanterar du namnutrymmen för dina förlängningsmetoder?

Tillämplighet

Man bör vara försiktig när man skapar förlängningsmetoder för att säkerställa att de passar alla möjliga ingångar och inte bara är relevanta för specifika situationer. Till exempel är det möjligt att utöka systemklasser som string , vilket gör din nya kod tillgänglig för vilken sträng som helst . Om din kod måste utföra domänspecifik logik i ett domänspecifikt strängformat, skulle en tilläggsmetod inte vara lämplig eftersom dess närvaro skulle förvirra samtal som arbetar med andra strängar i systemet.

Följande lista innehåller grundläggande funktioner och egenskaper för förlängningsmetoder

  1. Det måste vara en statisk metod.
  2. Det måste vara placerat i en statisk klass.
  3. Den använder nyckelordet "detta" som den första parametern med en typ i .NET och den här metoden kommer att anropas av en given typinstans på klientsidan.
  4. Det visas också av VS intellisense. När vi trycker på dot . efter en typinstans kommer den i VS-intelligens.
  5. En förlängningsmetod bör vara i samma namnutrymme som den används eller så måste du importera klassens namnutrymme med ett användande uttalande.
  6. Du kan ge valfritt namn på klassen som har en förlängningsmetod men klassen ska vara statisk.
  7. Om du vill lägga till nya metoder till en typ och inte har källkoden för det, är lösningen att använda och implementera förlängningsmetoder av den typen.
  8. Om du skapar förlängningsmetoder som har samma signaturmetoder som den typ du utökar, kommer förlängningsmetoderna aldrig att kallas.

Förlängningsmetoder - översikt

Förlängningsmetoder infördes i C # 3.0. Förlängningsmetoderna utökar och lägger till beteenden till befintliga typer utan att skapa en ny härledd typ, kompilera om eller på annat sätt ändra den ursprungliga typen. De är särskilt användbara när du inte kan ändra källan till den typ du vill förbättra. Förlängningsmetoder kan skapas för systemtyper, typer definierade av tredje part och typer som du själv har definierat. Förlängningsmetoden kan åberopas som om det var en medlemsmetod av den ursprungliga typen. Detta möjliggör metodkedja som används för att implementera ett flytande gränssnitt .

En förlängningsmetod skapas genom att lägga till en statisk metod i en statisk klass som skiljer sig från den ursprungliga typen som utvidgas. Den statiska klassen som innehar förlängningsmetoden skapas ofta för det enda syftet att hålla förlängningsmetoder.

Förlängningsmetoder tar en speciell första parameter som anger den ursprungliga typen som utvidgas. Denna första parameter är dekorerad med nyckelordet this (som utgör en speciell och distinkt användning av this i C # - det borde förstås som annorlunda än användningen av this som gör det möjligt att hänvisa till medlemmar i den aktuella objektinstansen).

I följande exempel är den ursprungliga typen som utökas string . String har förlängts med en metod Shorten() , som ger den extra funktionen för förkortning. Den statiska klassen StringExtensions har skapats för att hålla tilläggsmetoden. Förlängningsmetoden Shorten() visar att det är en förlängning av string via den speciellt markerade första parametern. För att visa att metoden Shorten() förlänger string markeras den första parametern med this . Därför är den första signatorn för den första parametern den this string text , där string är den ursprungliga typen som utvidgas och text är det valda parameternamnet.

static class StringExtensions
{
    public static string Shorten(this string text, int length) 
    {
        return text.Substring(0, length);
    }
}

class Program
{
    static void Main()
    {
        // This calls method String.ToUpper()
        var myString = "Hello World!".ToUpper();

        // This calls the extension method StringExtensions.Shorten()
        var newString = myString.Shorten(5); 

        // It is worth noting that the above call is purely syntactic sugar
        // and the assignment below is functionally equivalent
        var newString2 = StringExtensions.Shorten(myString, 5);
    }
}

Live-demonstration på .NET Fiddle


Objektet som passeras som det första argumentet för en förlängningsmetod (som åtföljs av det this nyckelordet) är den instans som tilläggsmetoden krävs.

Till exempel när denna kod körs:

"some string".Shorten(5);

Värdena för argumenten är som nedan:

text: "some string"
length: 5

Observera att förlängningsmetoder endast är användbara om de finns i samma namnutrymme som deras definition, om namnutrymmet importeras uttryckligen av koden med hjälp av förlängningsmetoden, eller om tilläggsklassen är mindre än namnområdet. .NET-ramriktlinjerna rekommenderar att du lägger tilläggsklasser i sitt eget namnområde. Detta kan dock leda till upptäcktsproblem.

Detta resulterar i inga konflikter mellan förlängningsmetoderna och biblioteken som används, såvida inte namnutrymmen som kan komma i konflikt uttryckligen dras in. Till exempel LINQ-tillägg :

using System.Linq; // Allows use of extension methods from the System.Linq namespace

class Program
{
    static void Main()
    {
        var ints = new int[] {1, 2, 3, 4};

        // Call Where() extension method from the System.Linq namespace
        var even = ints.Where(x => x % 2 == 0); 
    }
}

Live-demonstration på .NET Fiddle


Sedan C # 6.0 är det också möjligt att sätta ett using static direktiv med klassen som innehåller förlängningsmetoderna. using static System.Linq.Enumerable; till exempel using static System.Linq.Enumerable; . Detta gör förlängningsmetoder från den specifika klassen tillgängliga utan att andra typer från samma namnutrymme kommer att omfattas.


När en klassmetod med samma signatur är tillgänglig, prioriterar kompilatorn den över anknytningen till förlängningsmetoden. Till exempel:

class Test
{
   public void Hello()
   {
       Console.WriteLine("From Test");
   }
}

static class TestExtensions
{
    public static void Hello(this Test test)
    {
        Console.WriteLine("From extension method");
    }
}

class Program
{
    static void Main()
    {
        Test t = new Test();
        t.Hello(); // Prints "From Test"
    }
}

Live-demo på .NET Fiddle


Observera att om det finns två förlängningsfunktioner med samma signatur, och en av dem finns i samma namnområde, kommer den att prioriteras. Å andra sidan, om båda får åtkomst genom att using , kommer ett kompileringstidsfel med meddelandet:

Samtalet är tvetydigt mellan följande metoder eller egenskaper


Observera att den syntaktiska bekvämligheten med att ringa en förlängningsmetod via originalTypeInstance.ExtensionMethod() är en valfri bekvämlighet. Metoden kan också kallas på traditionellt sätt, så att den speciella första parametern används som en parameter för metoden.

Dvs båda följande arbete:

//Calling as though method belongs to string--it seamlessly extends string
String s = "Hello World";
s.Shorten(5);  

//Calling as a traditional static method with two parameters
StringExtensions.Shorten(s, 5);

Med hjälp av en förlängningsmetod

Förlängningsmetoder kan också användas som vanliga metoder för statisk klass. Detta sätt att kalla en förlängningsmetod är mer uttryckligt, men är i vissa fall nödvändigt.

static class StringExtensions
{
    public static string Shorten(this string text, int length) 
    {
        return text.Substring(0, length);
    }
}

Användande:

var newString = StringExtensions.Shorten("Hello World", 5);

När man ska anropa förlängningsmetoder som statiska metoder

Det finns fortfarande scenarier där du skulle behöva använda en förlängningsmetod som en statisk metod:

  • Lösa konflikter med en medlemsmetod. Detta kan hända om en ny version av ett bibliotek introducerar en ny medlemsmetod med samma signatur. I detta fall kommer komponenten att föredra medlemsmetoden.
  • Lösa konflikter med en annan förlängningsmetod med samma signatur. Detta kan hända om två bibliotek innehåller liknande förlängningsmetoder och namnutrymmen för båda klasserna med förlängningsmetoder används i samma fil.
  • Att skicka tilläggsmetod som metodgrupp till delegatparameter.
  • Att göra din egen bindning genom Reflection .
  • Använda förlängningsmetoden i fönstret Omedelbar i Visual Studio.

Med statisk

Om ett using static direktiv används för att föra statiska medlemmar i en statisk klass över hela världen, hoppas över förlängningsmetoder. Exempel:

using static OurNamespace.StringExtensions; // refers to class in previous example

// OK: extension method syntax still works.
"Hello World".Shorten(5);
// OK: static method syntax still works.
OurNamespace.StringExtensions.Shorten("Hello World", 5);
// Compile time error: extension methods can't be called as static without specifying class.
Shorten("Hello World", 5);

Om du tar bort den this modifieraren från det första argumentet i Shorten metoden kommer den sista raden att sammanställas.

Nollkontroll

Extensionsmetoder är statiska metoder som uppträder som instansmetoder. Till skillnad från vad som händer när man kallar en instansmetod på en null , när en förlängningsmetod kallas med en null , kastar den inte en NullReferenceException . Detta kan vara ganska användbart i vissa scenarier.

Tänk till exempel på följande statiska klass:

public static class StringExtensions
{
    public static string EmptyIfNull(this string text)
    {
        return text ?? String.Empty;
    }

    public static string NullIfEmpty(this string text)
    {
        return String.Empty == text ? null : text;
    }
}
string nullString = null;
string emptyString = nullString.EmptyIfNull();// will return ""
string anotherNullString = emptyString.NullIfEmpty(); // will return null

Live-demonstration på .NET Fiddle

Förlängningsmetoder kan bara se offentliga (eller interna) medlemmar i den utökade klassen

public class SomeClass
{
    public void DoStuff()
    {
        
    }

    protected void DoMagic()
    {
        
    }
}

public static class SomeClassExtensions
{
    public static void DoStuffWrapper(this SomeClass someInstance)
    {
        someInstance.DoStuff(); // ok
    }

    public static void DoMagicWrapper(this SomeClass someInstance)
    {
        someInstance.DoMagic(); // compilation error
    }
}

Förlängningsmetoder är bara ett syntaktiskt socker och är inte faktiskt medlemmar i den klass de utökar. Detta innebär att de inte kan bryta inkapslingen - de har bara tillgång till public (eller när de implementeras i samma montering, internal ) fält, egenskaper och metoder.

Generiska förlängningsmetoder

Precis som andra metoder kan förlängningsmetoder använda generika. Till exempel:

static class Extensions
{
    public static bool HasMoreThanThreeElements<T>(this IEnumerable<T> enumerable)
    {
        return enumerable.Take(4).Count() > 3;
    }
}

och att kalla det skulle vara som:

IEnumerable<int> numbers = new List<int> {1,2,3,4,5,6};
var hasMoreThanThreeElements = numbers.HasMoreThanThreeElements();

Visa demo

På samma sätt för flera typargument:

public static TU GenericExt<T, TU>(this T obj)
{
     TU ret = default(TU);
     // do some stuff with obj
     return ret;
}

Att kalla det skulle vara som:

IEnumerable<int> numbers = new List<int> {1,2,3,4,5,6};
var result = numbers.GenericExt<IEnumerable<int>,String>();

Visa demo

Du kan också skapa förlängningsmetoder för delvis bundna typer i flera generiska typer:

class MyType<T1, T2>
{
}

static class Extensions
{
    public static void Example<T>(this MyType<int, T> test)
    {        
    }
}

Att kalla det skulle vara som:

MyType<int, string> t = new MyType<int, string>();
t.Example();

Visa demo

Du kan också ange typbegränsningar med where :

public static bool IsDefault<T>(this T obj) where T : struct, IEquatable<T>
{
     return EqualityComparer<T>.Default.Equals(obj, default(T));
}

Ringkod:

int number = 5;
var IsDefault = number.IsDefault();

Visa demo

Förlängningsmetoder skickas ut baserat på statisk typ

Den statiska typen (kompileringstid) används snarare än den dynamiska (körtidstypen) för att matcha parametrar.

public class Base 
{ 
    public virtual string GetName()
    {
        return "Base";
    }
}

public class Derived : Base
{ 
    public override string GetName()
    {
        return "Derived";
    }
}

public static class Extensions
{
    public static string GetNameByExtension(this Base item)
    {
        return "Base";
    }

    public static string GetNameByExtension(this Derived item)
    {
        return "Derived";
    }
}

public static class Program   
{
    public static void Main()
    {
        Derived derived = new Derived();
        Base @base = derived;

        // Use the instance method "GetName"
        Console.WriteLine(derived.GetName()); // Prints "Derived"
        Console.WriteLine(@base.GetName()); // Prints "Derived"

        // Use the static extension method "GetNameByExtension"
        Console.WriteLine(derived.GetNameByExtension()); // Prints "Derived"
        Console.WriteLine(@base.GetNameByExtension()); // Prints "Base"
    }
}

Live-demonstration på .NET Fiddle

Sändningen baserad på statisk typ tillåter inte heller en förlängningsmetod att kallas på ett dynamic objekt:

public class Person
{
    public string Name { get; set; }
}

public static class ExtenionPerson
{
    public static string GetPersonName(this Person person)
    {
        return person.Name;
    }
}

dynamic person = new Person { Name = "Jon" };
var name = person.GetPersonName(); // RuntimeBinderException is thrown

Tilläggsmetoder stöds inte av dynamisk kod.

static class Program
{
    static void Main()
    {
        dynamic dynamicObject = new ExpandoObject();

        string awesomeString = "Awesome";

        // Prints True
        Console.WriteLine(awesomeString.IsThisAwesome());

        dynamicObject.StringValue = awesomeString;

        // Prints True
        Console.WriteLine(StringExtensions.IsThisAwesome(dynamicObject.StringValue)); 
        
        // No compile time error or warning, but on runtime throws RuntimeBinderException
        Console.WriteLine(dynamicObject.StringValue.IsThisAwesome());
    }
}

static class StringExtensions
{
    public static bool IsThisAwesome(this string value)
    {
        return value.Equals("Awesome");
    }
}

Anledningen till att [ringa förlängningsmetoder från dynamisk kod] fungerar inte är att i vanliga, icke-dynamiska kodförlängningsmetoder fungerar genom att göra en fullständig sökning av alla klasser som är kända för kompilatorn för en statisk klass som har en förlängningsmetod som matchar . Sökningen går i ordning baserat på namnutrymmet som är häckande och tillgängligt med using direktiv i varje namnområde.

Det innebär att för att få en dynamisk anknytning till förlängningsmetod rätt lösas måste DLR på något sätt veta vid körning vad alla namnområdesbo och using direktiv fanns i din källkod . Vi har ingen mekanism för att koda all information till samtalssidan. Vi övervägde att uppfinna en sådan mekanism, men beslutade att det var för höga kostnader och producerade för mycket schemaläggningsrisk för att vara värt det.

Källa

Förlängningsmetoder som starkt typade omslag

Förlängningsmetoder kan användas för att skriva starkt inslagna omslag för ordboksliknande objekt. Till exempel en cache, HttpContext.Items vid cetera ...

public static class CacheExtensions
{
    public static void SetUserInfo(this Cache cache, UserInfo data) => 
        cache["UserInfo"] = data;

    public static UserInfo GetUserInfo(this Cache cache) => 
        cache["UserInfo"] as UserInfo;
}

Detta tillvägagångssätt avlägsnar behovet av att använda strängbokstäver som nycklar över hela kodbasen såväl som behovet av gjutning till önskad typ under läsoperationen. Sammantaget skapar det ett säkrare, starkt typiskt sätt att interagera med så löst typade objekt som ordböcker.

Förlängningsmetoder för kedja

När en förlängningsmetod returnerar ett värde som har samma typ som this argument, kan det användas för att "kedja" ett eller flera metodsamtal med en kompatibel signatur. Detta kan vara användbart för förseglade och / eller primitiva typer och möjliggör skapandet av så kallade "flytande" API: er om metodnamnen läses som naturligt mänskligt språk.

void Main()
{
    int result = 5.Increment().Decrement().Increment(); 
    // result is now 6
}

public static class IntExtensions 
{
    public static int Increment(this int number) {
        return ++number;
    }

    public static int Decrement(this int number) {
        return --number;
    }
}

Eller så här

void Main()
{
    int[] ints = new[] { 1, 2, 3, 4, 5, 6};
    int[] a = ints.WhereEven();
    //a is { 2, 4, 6 };
    int[] b = ints.WhereEven().WhereGreaterThan(2);
    //b is { 4, 6 };
}

public static class IntArrayExtensions
{
    public static int[] WhereEven(this int[] array)
    {
        //Enumerable.* extension methods use a fluent approach
        return array.Where(i => (i%2) == 0).ToArray();
    }

    public static int[] WhereGreaterThan(this int[] array, int value)
    {
        return array.Where(i => i > value).ToArray();
    }
}

Förlängningsmetoder i kombination med gränssnitt

Det är mycket bekvämt att använda förlängningsmetoder med gränssnitt eftersom implementering kan lagras utanför klassen och allt som krävs för att lägga till viss funktionalitet i klassen är att dekorera klass med gränssnitt.

public interface IInterface
{
   string Do()
}

public static class ExtensionMethods{
    public static string DoWith(this IInterface obj){
      //does something with IInterface instance
    }
}

public class Classy : IInterface
{
   // this is a wrapper method; you could also call DoWith() on a Classy instance directly,
   // provided you import the namespace containing the extension method
   public Do(){
       return this.DoWith();
   }
}

använda som:

 var classy = new Classy();
 classy.Do(); // will call the extension
 classy.DoWith(); // Classy implements IInterface so it can also be called this way

IList Exempel på förlängningsmetod: Jämförelse av 2 listor

Du kan använda följande förlängningsmetod för att jämföra innehållet i två IList <T> instanser av samma typ.

Som standard jämförs objekten baserat på deras ordning i listan och objekten själva, som går falskt till parametern isOrdered jämför bara objekten själva oavsett ordning.

För att den här metoden ska fungera måste den generiska typen ( T ) åsidosätta både Equals och GetHashCode metoder.

Användande:

List<string> list1 = new List<string> {"a1", "a2", null, "a3"};
List<string> list2 = new List<string> {"a1", "a2", "a3", null};

list1.Compare(list2);//this gives false
list1.Compare(list2, false);//this gives true. they are equal when the order is disregarded

Metod:

public static bool Compare<T>(this IList<T> list1, IList<T> list2, bool isOrdered = true) 
{
    if (list1 == null && list2 == null)
        return true;
    if (list1 == null || list2 == null || list1.Count != list2.Count)
        return false;

    if (isOrdered)
    {
        for (int i = 0; i < list2.Count; i++)
        {
            var l1 = list1[i]; 
            var l2 = list2[i];
            if (
                 (l1 == null && l2 != null) || 
                 (l1 != null && l2 == null) || 
                 (!l1.Equals(l2)))
            {
                    return false;
            }
        }
        return true;
    }
    else
    {
        List<T> list2Copy = new List<T>(list2);
        //Can be done with Dictionary without O(n^2)
        for (int i = 0; i < list1.Count; i++)
        {
            if (!list2Copy.Remove(list1[i]))
                return false;
        }
        return true;
    }
}

Förlängningsmetoder med uppräkning

Förlängningsmetoder är användbara för att lägga till funktionalitet i uppräkningar.

En vanlig användning är att implementera en konverteringsmetod.

public enum YesNo
{
    Yes,
    No,
}

public static class EnumExtentions
{
    public static bool ToBool(this YesNo yn)
    {
        return yn == YesNo.Yes;
    }
    public static YesNo ToYesNo(this bool yn)
    {
        return yn ? YesNo.Yes : YesNo.No;
    }
}

Nu kan du snabbt konvertera ditt enumvärde till en annan typ. I det här fallet en bool.

bool yesNoBool = YesNo.Yes.ToBool(); // yesNoBool == true
YesNo yesNoEnum = false.ToYesNo();   // yesNoEnum == YesNo.No

Alternativt kan förlängningsmetoder användas för att lägga till egenskapliknande metoder.

public enum Element
{
    Hydrogen,
    Helium,
    Lithium,
    Beryllium,
    Boron,
    Carbon,
    Nitrogen,
    Oxygen
    //Etc
}

public static class ElementExtensions
{
    public static double AtomicMass(this Element element)
    {
        switch(element)
        {
            case Element.Hydrogen:  return 1.00794;
            case Element.Helium:    return 4.002602;
            case Element.Lithium:   return 6.941;
            case Element.Beryllium: return 9.012182;
            case Element.Boron:     return 10.811;
            case Element.Carbon:    return 12.0107;
            case Element.Nitrogen:  return 14.0067;
            case Element.Oxygen:    return 15.9994;
            //Etc
        }
        return double.Nan;
    }
}

var massWater = 2*Element.Hydrogen.AtomicMass() + Element.Oxygen.AtomicMass();

Tillägg och gränssnitt möjliggör tillsammans DRY-kod och mixinliknande funktioner

Förlängningsmetoder gör det möjligt för dig att förenkla dina gränssnittsdefinitioner genom att bara inkludera nödvändig funktionalitet i själva gränssnittet och att du kan definiera bekvämhetsmetoder och överbelastningar som förlängningsmetoder. Gränssnitt med färre metoder är lättare att implementera i nya klasser. Om du håller överbelastningar som tillägg istället för att inkludera dem i gränssnittet, sparar du direkt från att kopiera pannplåtkoden till varje implementering, vilket hjälper dig att behålla din kod TÖR. Detta liknar faktiskt mixinmönstret som C # inte stöder.

System.Linq.Enumerable tillägg till IEnumerable<T> är ett bra exempel på detta. IEnumerable<T> kräver endast implementeringsklassen för att implementera två metoder: generisk och icke-generisk GetEnumerator() . Men System.Linq.Enumerable ger otaliga användbara verktyg som tillägg som möjliggör en kort och tydlig förbrukning av IEnumerable<T> .

Följande är ett mycket enkelt gränssnitt med överbelastning av bekvämligheter som tillägg.

public interface ITimeFormatter
{
   string Format(TimeSpan span);
}

public static class TimeFormatter
{
    // Provide an overload to *all* implementers of ITimeFormatter.
    public static string Format(
        this ITimeFormatter formatter,
        int millisecondsSpan)
        => formatter.Format(TimeSpan.FromMilliseconds(millisecondsSpan));
}

// Implementations only need to provide one method. Very easy to
// write additional implementations.
public class SecondsTimeFormatter : ITimeFormatter
{
   public string Format(TimeSpan span)
   {
       return $"{(int)span.TotalSeconds}s";
   }
}

class Program
{
    static void Main(string[] args)
    {
        var formatter = new SecondsTimeFormatter();
        // Callers get two method overloads!
        Console.WriteLine($"4500ms is rougly {formatter.Format(4500)}");
        var span = TimeSpan.FromSeconds(5);
        Console.WriteLine($"{span} is formatted as {formatter.Format(span)}");
    }
}

Förlängningsmetoder för hantering av specialfall

Förlängningsmetoder kan användas för att "dölja" bearbetning av ineleganta affärsregler som annars skulle kräva rörig uppringningsfunktion med if / sedan uttalanden. Detta liknar och är analogt med hantering av noll med förlängningsmetoder. Till exempel,

public static class CakeExtensions
{
    public static Cake EnsureTrueCake(this Cake cake)
    {
        //If the cake is a lie, substitute a cake from grandma, whose cakes aren't as tasty but are known never to be lies. If the cake isn't a lie, don't do anything and return it.
        return CakeVerificationService.IsCakeLie(cake) ? GrandmasKitchen.Get1950sCake() : cake;
    }
}
Cake myCake = Bakery.GetNextCake().EnsureTrueCake();
myMouth.Eat(myCake);//Eat the cake, confident that it is not a lie.

Använda förlängningsmetoder med statiska metoder och återuppringningar

Överväg att använda förlängningsmetoder som funktioner som lägger in annan kod, här är ett bra exempel som använder både en statisk metod och en förlängningsmetod för att lasta in Try Catch-konstruktionen. Gör din kod Bullet Proof ...

using System;
using System.Diagnostics;

namespace Samples
{
    /// <summary>
    /// Wraps a try catch statement as a static helper which uses 
    /// Extension methods for the exception
    /// </summary>
    public static class Bullet
    {
        /// <summary>
        /// Wrapper for Try Catch Statement
        /// </summary>
        /// <param name="code">Call back for code</param>
        /// <param name="error">Already handled and logged exception</param>
        public static void Proof(Action code, Action<Exception> error)
        {
            try
            {
                code();
            }
            catch (Exception iox)
            {
                //extension method used here
                iox.Log("BP2200-ERR-Unexpected Error");
                //callback, exception already handled and logged
                error(iox);
            }
        }
        /// <summary>
        /// Example of a logging method helper, this is the extension method
        /// </summary>
        /// <param name="error">The Exception to log</param>
        /// <param name="messageID">A unique error ID header</param>
        public static void Log(this Exception error, string messageID)
        {
            Trace.WriteLine(messageID);
            Trace.WriteLine(error.Message);
            Trace.WriteLine(error.StackTrace);
            Trace.WriteLine("");
        }
    }
    /// <summary>
    /// Shows how to use both the wrapper and extension methods.
    /// </summary>
    public class UseBulletProofing
    {
        public UseBulletProofing()
        {
            var ok = false;
            var result = DoSomething();
            if (!result.Contains("ERR"))
            {
                ok = true;
                DoSomethingElse();
            }
        }

        /// <summary>
        /// How to use Bullet Proofing in your code.
        /// </summary>
        /// <returns>A string</returns>
        public string DoSomething()
        {
            string result = string.Empty;
            //Note that the Bullet.Proof method forces this construct.
            Bullet.Proof(() =>
            {
                //this is the code callback
                result = "DST5900-INF-No Exceptions in this code";
            }, error =>
            {
                //error is the already logged and handled exception
                //determine the base result
                result = "DTS6200-ERR-An exception happened look at console log";
                if (error.Message.Contains("SomeMarker"))
                {
                    //filter the result for Something within the exception message
                    result = "DST6500-ERR-Some marker was found in the exception";
                }
            });
            return result;
        }

        /// <summary>
        /// Next step in workflow
        /// </summary>
        public void DoSomethingElse()
        {
            //Only called if no exception was thrown before
        }
    }
}

Förlängningsmetoder på gränssnitt

En användbar funktion med förlängningsmetoder är att du kan skapa vanliga metoder för ett gränssnitt. Normalt kan ett gränssnitt inte ha delade implementationer, men med förlängningsmetoder kan de det.

public interface IVehicle
{
    int MilesDriven { get; set; }
}

public static class Extensions
{
    public static int FeetDriven(this IVehicle vehicle)
    {
        return vehicle.MilesDriven * 5028;
    }
}

I det här exemplet kan metoden FeetDriven användas på vilken IVehicle helst. Denna logik i denna metod skulle gälla alla IVehicle , så det kan göras på detta sätt så att det inte behöver finnas en FeetDriven i IVehicle definitionen som skulle implementeras på samma sätt för alla barn.

Använd förlängningsmetoder för att skapa vackra mapper klasser

Vi kan skapa en bättre kartläggningsklasser med förlängningsmetoder. Antag att om jag har några DTO-klasser som

 public class UserDTO
 {
        public AddressDTO Address { get; set; }
 }

 public class AddressDTO
 {
        public string Name { get; set; }
 }

och jag måste kartlägga motsvarande klasser för visningsmodeller

public class UserViewModel
{
    public AddressViewModel Address { get; set; }
}

public class AddressViewModel
{
    public string Name { get; set; }
}

då kan jag skapa min mapper-klass som nedan

public static class ViewModelMapper
{
      public static UserViewModel ToViewModel(this UserDTO user)
      {
            return user == null ?
                null :
                new UserViewModel()
                {
                    Address = user.Address.ToViewModel()
                    // Job = user.Job.ToViewModel(),
                    // Contact = user.Contact.ToViewModel() .. and so on
                };
      }

      public static AddressViewModel ToViewModel(this AddressDTO userAddr)
      {
            return userAddr == null ?
                null :
                new AddressViewModel()
                {
                    Name = userAddr.Name
                };
      }
}

Då kan jag äntligen åberopa min mapper som nedan

    UserDTO userDTOObj = new UserDTO() {
            Address = new AddressDTO() {
                Name = "Address of the user"
            }
        };

    UserViewModel user = userDTOObj.ToViewModel(); // My DTO mapped to Viewmodel

Skönheten här är alla kartläggningsmetoder har ett vanligt namn (ToViewModel) och vi kan återanvända det på flera sätt

Använda förlängningsmetoder för att bygga nya samlingstyper (t.ex. DictList)

Du kan skapa förlängningsmetoder för att förbättra användbarheten för kapslade samlingar som en Dictionary med ett List<T> -värde.

Tänk på följande förlängningsmetoder:

public static class DictListExtensions
{
    public static void Add<TKey, TValue, TCollection>(this Dictionary<TKey, TCollection> dict, TKey key, TValue value)
            where TCollection : ICollection<TValue>, new()
    {
        TCollection list;
        if (!dict.TryGetValue(key, out list))
        {
            list = new TCollection();
            dict.Add(key, list);
        }

        list.Add(value);
    }

    public static bool Remove<TKey, TValue, TCollection>(this Dictionary<TKey, TCollection> dict, TKey key, TValue value)
        where TCollection : ICollection<TValue>
    {
        TCollection list;
        if (!dict.TryGetValue(key, out list))
        {
            return false;
        }

        var ret = list.Remove(value);
        if (list.Count == 0)
        {
            dict.Remove(key);
        }
        return ret;
    }
}

Du kan använda förlängningsmetoderna enligt följande:

var dictList = new Dictionary<string, List<int>>();

dictList.Add("example", 5);
dictList.Add("example", 10);
dictList.Add("example", 15);

Console.WriteLine(String.Join(", ", dictList["example"])); // 5, 10, 15

dictList.Remove("example", 5);
dictList.Remove("example", 10);

Console.WriteLine(String.Join(", ", dictList["example"])); // 15

dictList.Remove("example", 15);

Console.WriteLine(dictList.ContainsKey("example")); // False

Visa demo



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow