2013-05-24 18 views
31

Powiedzmy, że potrzebuję prostej, prywatnej metody pomocniczej, a intuicyjnie w kodzie miałaby sens jako metoda rozszerzenia. Czy istnieje sposób na hermetyzację tego pomocnika dla jedynej klasy, która faktycznie potrzebuje go używać?Czy można stosować prywatne metody przedłużania?

Na przykład, spróbuj tego:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var value = 0; 
     value = value.GetNext(); // Compiler error 
    } 

    static int GetNext(this int i) 
    { 
     return i + 1; 
    } 
} 

kompilator nie "widzi" metodę GetNext() przedłużacza. Błąd jest:

metoda Rozszerzenie musi być zdefiniowana w non-rodzajowego klasy statycznej

Słusznie, więc zawinąć go w swojej własnej klasy, ale nadal zamknięte wewnątrz obiektu, gdzie należy:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var value = 0; 
     value = value.GetNext(); // Compiler error 
    } 

    static class Extensions 
    { 
     static int GetNext(this int i) 
     { 
      return i + 1; 
     } 
    } 
} 

Jeszcze żadnych kości. Teraz komunikat o błędzie:

Metoda rozszerzenia musi być zdefiniowana w klasie statycznej najwyższego poziomu; Rozszerzenia to klasa zagnieżdżona.

Czy istnieje wymowny powód takiego wymogu? Zdarzają się przypadki, w których metoda pomocnicza powinna być zamknięta w sposób prywatny, a są przypadki, w których kod jest dużo czystszy i bardziej czytelny/obsługiwany, jeśli metoda pomocnicza jest metodą rozszerzenia. W przypadkach, gdy te dwa przecinają się, czy oba mogą być spełnione, czy musimy wybrać jeden nad drugim?

+1

Przeczytaj co Message Pierwszy błąd został mówiąc jej chce cię Klasa Program statyczny, Próbowałeś zarówno statyczne i próby Twojego kodu? – Bearcat9425

+0

Powiązane pytanie: http://stackoverflow.com/questions/2145576/how-can-i-implement-anextext-property-class-for-primitive-types-in-a-clean-w – cvraman

+1

@ Bearcat9425, że * naprawiłoby "ten konkretny scenariusz użycia, ale nie jest ogólnie stosowane.Myślę, że to pytanie dotyczy ogólnej aplikacji, a nie przykładowych aplikacji konsolowych: –

Odpowiedz

15

Wierzę, że najlepsze, co można uzyskać w ogólnym przypadku, jest klasa internal static z metodami rozszerzenia internal static. Ponieważ będzie to w twoim własnym zespole, jedynymi osobami, których potrzebujesz, aby uniemożliwić korzystanie z rozszerzenia, są autorzy zespołu - tak, że niektóre jawnie nazwane przestrzenie nazw (takie jak My.Extensions.ForFoobarOnly) mogą wystarczyć, aby uniknąć niewłaściwego użycia.

Minimalna internal ograniczenie pokryte implement extension artykule

klasie musi być widoczna dla kodu klienta ... metody z co najmniej takiej samej widoczności jak zawierającej klasy.

Uwaga: Chciałbym zrobić przedłużacz do publicznej wiadomości w każdym razie w celu uproszczenia testów jednostkowych, ale umieścić w jakimś wyraźnie nazwie nazw jak Xxxx.Yyyy.Internal więc innych użytkowników zespole nie oczekiwać, że metody, które będą wspierane/wymagalne. Zasadniczo polegaj na konwencji innej niż czas kompilacji.

+0

Prawdopodobnie będę grał z konwencjami przez długi czas, by w końcu dojść do czegoś, co ma sens. To dość rzadki przypadek, że nie martwię się zbytnio, to tylko kwestia pozostawienia kodu w intuicyjnym i możliwym do wsparcia stanie dla każdego, kto będzie go obsługiwał w przyszłości (w tym ja). Dzięki! – David

+0

Alexei: czy w przypadku testów jednostkowych nie byłoby lepiej pozostawić wewnętrzne rozszerzenie i odsłonić elementy wewnętrzne do projektu testów za pomocą InternalsVisibleToAttribute (https://msdn.microsoft.com/en-us/library/system.runtime. compilerservices.internalsvisibletoattribute (v = vs.110) .aspx)? – Przemo

+0

@Przemo 'InternalsVisibleToAttribute' w rzeczywistości może być używane, ale zabija niektóre z możliwości użycia metod rozszerzeń, ponieważ nie są one już wyświetlane w autouzupełnianiu, o ile wiem. Jest to mniej lub bardziej osobisty wybór dla większości projektów (poza niektórymi bibliotekami zaprojektowanymi do bardzo szerokiego zastosowania) - musisz rozważyć wygodę i czystość API w twoim konkretnym przypadku. Projekty, nad którymi pracuję są ogólnie "małe" i nie ujawniają prawdziwych publicznych API - więc "public" działa dobrze w moich przypadkach. –

1

Ten kod kompiluje i działa:

static class Program 
{ 
    static void Main(string[] args) 
    { 
     var value = 0; 
     value = value.GetNext(); // Compiler error 
    } 

    static int GetNext(this int i) 
    { 
     return i + 1; 
    } 
} 

Zwróć uwagę na static class Program linia, która była co kompilator powiedział jest potrzebne.

+1

To działa dobrze w moim wymyślnym przykładzie, przyznaję. Ale uczynienie klasy statycznej nie zawsze jest realną opcją. W przypadku modeli domen może powodować duże problemy. – David

+0

Dlatego są one nazywane "metodami rozszerzeń", a najlepszą praktyką jest definiowanie ich w oddzielnej klasie statycznej, umieszczonej pod innym obszarem nazw. –

+1

"Najlepsze praktyki" to pojęcie względne. Kapsułkowanie jest również najlepszą praktyką. Zdarzają się sytuacje, w których umieszczanie funkcji pomocniczych (w tym przypadku metod rozszerzeń) w oddzielnej klasie (lub całkowicie oddzielnej przestrzeni nazw) powoduje nieszczelną abstrakcję, ścisłe powiązanie tej funkcji pomocnika z obiektami wewnętrznymi jedynego obiektu, który pomaga. – David

34

Czy istnieje wymowny powód takiego wymogu?

To niewłaściwe pytanie. Pytanie zadane przez zespół projektowania języka podczas projektowania tej funkcji było następujące:

Czy istnieje ważny powód, aby umożliwić deklarowanie metod rozszerzeń w zagnieżdżonych typach statycznych?

Ponieważ rozszerzenie metod zostały zaprojektowane tak, aby prace LINQ i LINQ nie ma scenariuszy, w których metody rozszerzenie byłoby prywatny do rodzaju, odpowiedź była „nie, nie ma takiej ważny powód”.

Eliminując możliwość umieszczania metod rozszerzania w typach zagnieżdżonych statycznych, żadna z reguł wyszukiwania metod rozszerzeń w typach zagnieżdżonych statycznych nie musi być, argumentowana, zaprojektowana, wyspecyfikowana, zaimplementowana, przetestowana, udokumentowana, wysłana do klientów lub dostosowany do każdej przyszłej funkcji C#. To było znaczące oszczędności.

+7

'" szukanie metod rozszerzenia "' - Jest część, o której nie myślałem. Łatwo krzyczeć zza trybuny, że "to powinno zadziałać", zapominając o wysiłku, który jest potrzebny, aby to zadziałało. Zupełnie ma to sens teraz, w połączeniu z odpowiedzią Alexeia dociera do bardzo praktycznego rozwiązania. Dzięki! – David

+4

To, co mi mówię, to to, że czas projektowania naprawdę nie projektował metod rozszerzenia jako takich, zaprojektował LINQ. Przypadki użycia metod rozszerzenia jako prywatnych są takie same dla każdej innej metody, która jest prywatna. Nie ma nic szczególnego w metodach rozszerzania. – Puppy

+0

Teoretycznie jest to właściwa odpowiedź. Zespół projektowy musi ustalić granice tego, co kod może zrobić, aby było możliwe do opanowania. – DerpyNerd

1

Uważam, że jest to sposób, w jaki wdrożyli kompilację metod rozszerzeń.

Patrząc na IL, wydaje się, że dodają one dodatkowe atrybuty do metody.

.method public hidebysig static int32 GetNext(int32 i) cil managed 
{ 
    .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() 
    .maxstack 2 
    .locals init (
     [0] int32 num) 
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldc.i4.1 
    L_0003: add 
    L_0004: dup 
    L_0005: starg.s i 
    L_0007: stloc.0 
    L_0008: br.s L_000a 
    L_000a: ldloc.0 
    L_000b: ret 
} 

Nie ma chyba jakiś bardzo fundamentalne, że brakuje nam, że po prostu nie ma to działać, dlatego ograniczenie jest na swoim miejscu. Możliwe też, że chcieli wymusić praktyki kodowania. Niestety, po prostu nie działa i musi znajdować się w klasach statycznych najwyższego poziomu.

0

Jest to wzięte z przykładu na microsoft msdn. Metody Extesnion muszą być zdefiniowane w klasie statycznej. Zobacz jak statyczna klasa została zdefiniowana w innej przestrzeni nazw i importowane. Można zobaczyć przykład tutaj http://msdn.microsoft.com/en-us/library/bb311042(v=vs.90).aspx

namespace TestingEXtensions 
{ 
    using CustomExtensions; 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var value = 0; 
      Console.WriteLine(value.ToString()); //Test output 
      value = value.GetNext(); 
      Console.WriteLine(value.ToString()); // see that it incremented 
      Console.ReadLine(); 
     } 
    } 
} 

namespace CustomExtensions 
{ 
    public static class IntExtensions 
    { 
     public static int GetNext(this int i) 
     { 
      return i + 1; 
     } 
    } 
} 
Powiązane problemy