Jak rozszerzyć klasę za pomocą metod rozszerzających C #?

98

Czy do klasy można zastosować metody rozszerzające?

Na przykład rozszerz DateTime, aby uwzględnić metodę Tomorrow (), którą można wywołać, na przykład:

DateTime.Tomorrow();

Wiem, że mogę użyć

static DateTime Tomorrow(this Datetime value) { //... }

Lub

public static MyClass {
  public static Tomorrow() { //... }
}

dla podobnego wyniku, ale jak mogę przedłużyć DateTime, aby móc wywołać DateTime.Tomorrow?

David Glenn
źródło

Odpowiedzi:

70

Nie można dodawać metod do istniejącego typu, chyba że istniejący typ jest oznaczony jako częściowy, można dodawać tylko metody, które wydają się być członkiem istniejącego typu za pomocą metod rozszerzających. Ponieważ jest to przypadek, nie można dodać metod statycznych do samego typu, ponieważ metody rozszerzające używają wystąpień tego typu.

Nic nie stoi na przeszkodzie, aby utworzyć własną statyczną metodę pomocniczą, taką jak ta:

static class DateTimeHelper
{
    public static DateTime Tomorrow
    {
        get { return DateTime.Now.AddDays(1); }
    }
}

Którego możesz użyć w ten sposób:

DateTime tomorrow = DateTimeHelper.Tomorrow;
Andrew Hare
źródło
6
huh woot? chyba że został zrealizowany w ciągu 6 miesięcy od tego i Kumu odpowiedź jest właśnie tam, to wygląda rzeczywiście niekompletny!
cregox
4
@Cawas to nie jest niepełne, Andrew pokazuje, jak to zrobić za pomocą statycznego pomocnika, a nie metody rozszerzającej (ponieważ nie ma instancji).
Nick N.
1
Masz rację, Nick. Jednak wolę metody rozszerzające! ;)
cregox
2
A co z extensionmethod.net/csharp/datetime ? IMHO, lepsze próbki w celu zminimalizowania krzywej uczenia się to rzeczywiste aplikacje z pełnym kodem źródłowym i dobrymi wzorcami
Kiquenet
3
Problem z tym kodem polega na tym, że działa on tylko na DateTime.Now, a nie na żadnym obiekcie DateTime. Jako narzędzie można go użyć do określenia dnia po jakimś poprzednim (lub przyszłym) dniu. Nie wspominając DateTime.Now jest ustalana za każdym razem to nazwać ...
MintGrowth
181

Użyj metody rozszerzenia .

Dawny:

namespace ExtensionMethods
{
    public static class MyExtensionMethods
    {
        public static DateTime Tomorrow(this DateTime date)
        {
            return date.AddDays(1);
        }    
    }
}

Stosowanie:

DateTime.Now.Tomorrow();

lub

AnyObjectOfTypeDateTime.Tomorrow();
Kumu
źródło
2
Shuggy odpowiedź „s niszczyć również pewne światło na sposób podobny do tego rozwiązania.
cregox
8
Nie zapomnij o 'użyciu ExtensionMethods;' u góry dokumentu.
Luke Alderton,
dlaczego nie mogę zrobić DateTime.Tomorrow ()?
lawphotog
Cześć lawphotog, to rozszerzenie wymaga obiektu, tutaj DateTime jest strukturą, a nie obiektem.
Kumu
4
Jak wspomniano w poprzednich komentarzach (najwyraźniej nie było to dla mnie wystarczająco jasne), NIE będziesz mógł używać, DateTime.Tomorrow()ponieważ metody rozszerzające działają tylko na INSTANCJACH klasy i jej struktury. Aby „rozszerzyć” statyczną metodę w strukturze klasy, postępuj zgodnie z odpowiedzią Andrew lub odpowiedzią Shuggy'ego .
Alex
18

Metody rozszerzające są lukrem składniowym służącym do tworzenia metod statycznych, których pierwszy parametr jest instancją typu T, tak, jakby były metodą instancji w T.

W związku z tym korzyść jest w dużej mierze utracona, gdy tworzysz `` statyczne metody rozszerzające '', ponieważ mogłyby one zmylić czytelnika kodu nawet bardziej niż metoda rozszerzająca (ponieważ wydają się w pełni kwalifikowane, ale nie są w rzeczywistości zdefiniowane w tej klasie) bez zysku syntaktycznego (na przykład możliwość łączenia połączeń w płynny styl w Linq).

Ponieważ i tak musiałbyś wprowadzić rozszerzenia do zakresu za pomocą using, argumentowałbym, że tworzenie jest prostsze i bezpieczniejsze:

public static class DateTimeUtils
{
    public static DateTime Tomorrow { get { ... } }
}

A następnie użyj tego w swoim kodzie poprzez:

WriteLine("{0}", DateTimeUtils.Tomorrow)
ShuggyCoUk
źródło
11

Odpowiedź jest najbliższa przez dodanie metody rozszerzającej do System.Typeobiektu. Niezbyt ładne, ale wciąż interesujące.

public static class Foo
{
    public static void Bar()
    {
        var now = DateTime.Now;
        var tomorrow = typeof(DateTime).Tomorrow();
    }

    public static DateTime Tomorrow(this System.Type type)
    {
        if (type == typeof(DateTime)) {
            return DateTime.Now.AddDays(1);
        } else {
            throw new InvalidOperationException();
        }
    }
}

W przeciwnym razie IMO Andrew i ShuggyCoUk mają lepszą implementację.

Adrian Godong
źródło
Z takim podejściem są problemy. Konieczność wpisania „typeof (…)” nie jest wygodna, a dzięki funkcji Intellisense zobaczysz rozszerzenia każdego typu. Mimo to jest to interesujące podejście, o którym nie pomyślałem, +1.
Meta-Knight
@ Meta-Knight Prawda, dlatego osobiście wolę odpowiedź drugiej osoby. Moja odpowiedź miałaby składnię najbliższą pytaniu OP, ale nie jest to najlepszy sposób rozwiązania tego problemu.
Adrian Godong
Typemożna zastąpić innym wymaganym typem. Używam go Fromi działa doskonale. więc myślę, że ta odpowiedź jest ogólna, ale poprawna
Katia
3

Zrobiłbym to samo co Kumu

namespace ExtensionMethods
{
    public static class MyExtensionMethods
    {
        public static DateTime Tomorrow(this DateTime date)
        {
           return date.AddDays(1);
        }    
    }
}

ale nazwij to nową DateTime (). Tomorrow ();

Myślę, że robi więcej seansów niż DateTime.Now.Tomorrow ();

mimo
źródło
1
I przegapiłeś okazję, by napisać to jako komentarz do odpowiedzi Kumu! : P
cregox
3

Zapewniają możliwość rozszerzania istniejących typów poprzez dodawanie nowych metod bez konieczności modyfikowania typu. Wywoływanie metod z obiektów typu rozszerzonego w aplikacji przy użyciu składni metody instancji jest nazywane metodami „rozszerzającymi”. Metody rozszerzające nie są członkami instancji w typie. Kluczową kwestią do zapamiętania jest to, że metody rozszerzające, zdefiniowane jako metody statyczne, znajdują się w zakresie tylko wtedy, gdy przestrzeń nazw jest jawnie importowana do kodu źródłowego aplikacji za pośrednictwem dyrektywy using. Mimo że metody rozszerzające są zdefiniowane jako metody statyczne, nadal są wywoływane przy użyciu składni wystąpienia.

Sprawdź pełny przykład tutaj http://www.dotnetreaders.com/articles/Extension_methods_in_C-sharp.net,Methods_in_C_-sharp/201

Przykład:

class Extension
    {
        static void Main(string[] args)
        {
            string s = "sudhakar";
            Console.WriteLine(s.GetWordCount());
            Console.ReadLine();
        }

    }
    public static class MyMathExtension
    {

        public static int GetWordCount(this System.String mystring)
        {
            return mystring.Length;
        }
    }
sudhakar
źródło
3

Szukałem czegoś podobnego - listy ograniczeń dla klas, które zapewniają metody rozszerzające. Wydaje się, że trudno jest znaleźć zwięzłą listę, więc oto:

  1. Nie możesz mieć niczego prywatnego ani chronionego - pól, metod itp.

  2. Musi to być klasa statyczna, jak w public static class....

  3. W klasie mogą znajdować się tylko metody i wszystkie muszą być public static.

  4. Nie możesz mieć konwencjonalnych metod statycznych - te, które nie zawierają argumentu this, nie są dozwolone.

  5. Wszystkie metody muszą się rozpocząć:

    public static ReturnType MethodName (this ClassName _this, ...)

Zatem pierwszym argumentem jest zawsze to odniesienie.

Stwarza to ukryty problem - jeśli dodasz metody, które wymagają jakiegokolwiek rodzaju blokady, nie możesz tak naprawdę zapewnić jej na poziomie klasy. Zwykle zapewniasz blokadę na poziomie instancji prywatnej, ale nie jest możliwe dodanie żadnych pól prywatnych, co pozostawia kilka bardzo niewygodnych opcji, takich jak udostępnienie go jako publicznego statycznego na niektórych zewnętrznych klasach itp. Jest ryzykowny. Znaki, że język C # miał zły obrót w ich projektowaniu .

Obejściem problemu jest użycie klasy Extension Method jako fasady dla zwykłej klasy, a wszystkie statyczne metody w klasie Extension po prostu wywołują prawdziwą klasę, prawdopodobnie używając Singletona .

Chris Moschini
źródło
2

Niestety nie możesz tego zrobić. Uważam jednak, że byłoby to przydatne. Bardziej naturalne jest pisanie:

DateTime.Tomorrow

niż:

DateTimeUtil.Tomorrow

W przypadku klasy Util należy sprawdzić, czy istnieje metoda statyczna w dwóch różnych klasach, a nie w jednej.

Meta rycerz
źródło
1

Ulepszyliśmy naszą odpowiedź o szczegółowe wyjaśnienie, teraz łatwiej jest zrozumieć metodę rozszerzenia

Metoda rozszerzenia : jest to mechanizm, za pomocą którego możemy rozszerzyć zachowanie istniejącej klasy bez używania podklasy lub modyfikowania lub rekompilowania oryginalnej klasy lub struktury.

Możemy rozszerzyć nasze niestandardowe klasy, klasy frameworków .net itp.

Metoda rozszerzająca jest w rzeczywistości specjalnym rodzajem metody statycznej zdefiniowanej w klasie statycznej.

Ponieważ DateTimeklasa jest już zajęta powyżej i dlatego nie wzięliśmy tej klasy jako wyjaśnienia.

Poniżej przykład

// To jest istniejąca klasa kalkulatora, która ma tylko jedną metodę (Dodaj)

public class Calculator 
{
    public double Add(double num1, double num2)
    {
        return num1 + num2;
    }

}

// Below is the extension class which have one extension method.  
public static class Extension
{
    // It is extension method and it's first parameter is a calculator class.It's behavior is going to extend. 
    public static double Division(this Calculator cal, double num1,double num2){
       return num1 / num2;
    }   
}

// We have tested the extension method below.        
class Program
{
    static void Main(string[] args)
    {
        Calculator cal = new Calculator();
        double add=cal.Add(10, 10);
        // It is a extension method in Calculator class.
        double add=cal.Division(100, 10)

    }
}
Sheo Dayal Singh
źródło