Po co używać metody publicznej w klasie wewnętrznej?


250

W jednym z naszych projektów jest dużo kodu, który wygląda następująco:

internal static class Extensions
{
    public static string AddFoo(this string s)
    {
        if (s == null)
        {
            return "Foo";
        }

        return $({s}Foo);
    }
}

Czy jest jakiś wyraźny powód, aby to zrobić inaczej niż „łatwiej jest upublicznić ten typ później?”

Podejrzewam, że ma to znaczenie tylko w bardzo dziwnych przypadkach na krawędziach (odbicie w Silverlight) lub wcale.


1
Z mojego doświadczenia wynika, że ​​jest to normalna praktyka.
phoog

2
@ phoog, OK. ale dlaczego jest to normalna praktyka? Może po prostu łatwiej niż zmienić kilka metod na wewnętrzne? Szybka poprawka -> zamiast tego zaznaczyć typ wewnętrzny?
bopapa_1979

tak zawsze zakładałem. Nie mam jednak dobrze przemyślanej analizy, dlatego nie opublikowałem odpowiedzi.
phoog

2
Odpowiedź Erica Lipperta bardzo dobrze podsumowuje moje myślenie, a także przywołuje coś, co czaiło się w moim mózgu, próbując znaleźć wyjście: niejawne implementacje członków interfejsu muszą być jawne.
phoog

Czy ta metoda nie jest równa return s + "Foo";? +Operator nie dba o null lub pustych strunach.
Mikkel R. Lund

Odpowiedzi:


419

AKTUALIZACJA: To pytanie było przedmiotem mojego bloga we wrześniu 2014 r . Dzięki za świetne pytanie!

Trwa debata na ten temat, nawet w samym zespole kompilatorów.

Po pierwsze, dobrze jest zrozumieć zasady. Publiczny członek klasy lub struktury to członek, który jest dostępny dla wszystkiego, co może uzyskać dostęp do typu zawierającego . Publiczny członek klasy wewnętrznej jest więc efektywnie wewnętrzny.

Czy teraz, biorąc pod uwagę klasę wewnętrzną, jej członkowie, do których chcesz uzyskać dostęp w zgromadzeniu, powinni być oznaczeni jako publiczni czy wewnętrzni?

Moja opinia jest taka: oznacz takich członków jako publiczną.

Używam słowa „public”, co oznacza, że ​​„ten członek nie jest szczegółem implementacji”. Chroniony członek jest szczegółem implementacji; jest coś w tym, co będzie potrzebne, aby klasa pochodna działała. Element wewnętrzny jest szczegółem implementacji; coś innego wewnętrznego w tym zespole potrzebuje członka, aby działać poprawnie. Członek publiczny mówi: „ten członek reprezentuje kluczową, udokumentowaną funkcjonalność zapewnianą przez ten obiekt”.

Zasadniczo mam takie podejście: załóżmy, że postanowiłem przekształcić tę klasę wewnętrzną w klasę publiczną. Aby to zrobić, chcę zmienić dokładnie jedno : dostępność klasy. Jeśli przekształcenie klasy wewnętrznej w klasę publiczną oznacza, że ​​muszę również przekształcić członka wewnętrznego w członka publicznego, wówczas ten członek był częścią publicznego obszaru klasy i powinien był być przede wszystkim publiczny.

Inni ludzie się nie zgadzają. Istnieje kontyngent, który mówi, że chcą móc rzucić okiem na deklarację członka i od razu wiedzieć, czy będzie wywoływana tylko z kodu wewnętrznego.

Niestety nie zawsze działa to dobrze; na przykład klasa wewnętrzna, która implementuje interfejs wewnętrzny, nadal musi mieć elementy implementujące oznaczone jako publiczne, ponieważ są one częścią publicznej powierzchni klasy .


11
Podoba mi się ta odpowiedź ... dobrze pasuje do mojego podejścia do pisania kodu, który w największym możliwym stopniu sam się dokumentuje, a zakres jest świetnym sposobem na przekazanie zamiaru, zwłaszcza jeśli inni członkowie zespołu rozumieją twoją konwencję.
bopapa_1979

10
+1 za powiedzenie tego, co chciałem powiedzieć, ale sformułowanie go znacznie lepiej niż byłem w stanie (również za podniesienie kąta interfejsu, chociaż zauważam, że jest to prawdą tylko w przypadku niejawnych implementacji elementów członkowskich).
phoog

2
Część Interfejs jest ważna - nie można zaimplementować metody interfejsu za pomocą wewnętrznego, jest to deklaracja interfejsu publicznego lub jawnego. Tak więc słowo public jest przeciążone dwoma znaczeniami.
Michael Stum

8
Z perspektywy idealogicznej twój argument na rzecz publicjest bardzo przekonujący. Uważam jednak, że „nie zawsze dobrze się układa ...” jest szczególnie silny. Utrata spójności dewaluuje wszelkie korzyści „na pierwszy rzut oka”. Używanie internalw ten sposób oznacza celowe budowanie intuicji, które czasem są błędne. Posiadanie w większości właściwej intuicji jest IMO okropną rzeczą do programowania.
Brian

3
Ważny cytat z posta na blogu: „Radzę przedyskutować problem w zespole, podjąć decyzję, a następnie trzymać się go”.
bopapa_1979

17

Jeśli klasa jest internal, nie ma znaczenia z punktu widzenia dostępności, czy oznaczysz metodę internalczy public. Jednak nadal dobrze jest użyć typu, którego użyłbyś, gdyby klasa była public.

Podczas gdy niektórzy powiedzieli, że to ułatwia przejście od internaldo public. Służy również jako część opisu metody. Internalmetody są zwykle uważane za niebezpieczne dla nieskrępowanego dostępu, podczas gdy publicmetody są uważane za (głównie) darmową grę.

Używając internallub publictak, jak na publiczajęciach, upewniasz się, że komunikujesz, jaki styl dostępu jest oczekiwany, jednocześnie ułatwiając pracę wymaganą do stworzenia klasy publicw przyszłości.


Staram się ograniczać wszystko w jak największym stopniu, jednocześnie tworząc dobry interfejs API i pozwalając mojemu zamierzonemu konsumentowi robić to, co zamierzam. Jestem również z tobą w sprawie oznaczenia członka, tak jak zrobiłbym, gdyby klasa była publiczna. Mówiąc dokładniej, oznaczyłbym każdego członka jako publiczny tylko wtedy, gdy uważam to za zawsze bezpieczne. W przeciwnym razie ktoś inny oznaczający klasę publicznie później (zdarza się) może narazić nie-bezpiecznych członków.
bopapa_1979

@Eric: Jeśli chcesz zrezygnować z określania bezpieczeństwa metody, dopóki nie będziesz gotowy, to jest w porządku, ale mówiłem tylko o metodach uznanych za bezpieczne, w którym to przypadku nie ma możliwości ujawnienia.
Guvante

10

Często zaznaczam moje metody w klasach wewnętrznych jako publiczne zamiast wewnętrznych jako a) to naprawdę nie ma znaczenia i b) używam wewnętrznego, aby wskazać, że metoda jest wewnętrzna celowo (istnieje jakiś powód, dla którego nie chcę tego ujawniać metoda w klasie publicznej. Dlatego jeśli mam metodę wewnętrzną, naprawdę muszę zrozumieć powód, dla którego jest ona wewnętrzna, zanim zmienię ją na publiczną, natomiast jeśli mam do czynienia z metodą publiczną w klasie wewnętrznej, naprawdę muszę pomyśleć, dlaczego klasa jest wewnętrzna, w przeciwieństwie do tego, dlaczego każda metoda jest wewnętrzna.


Dziękuję za odpowiedź. Jednak
uparcie twierdzę,

1
Masz rację, Eric, to był trochę niepotrzebny komentarz. Mam nadzieję, że reszta mojej odpowiedzi przydała się. Myślę, że odpowiedź Erica Lipperta była tym, co próbowałem wyrazić, ale wyjaśnił to o wiele lepiej.
Ɖiamond ǤeezeƦ

8

Podejrzewam, że „łatwiej jest później upublicznić ten typ?” czy to jest

Reguły określania zakresu oznaczają, że metoda będzie widoczna tylko jako internal- więc naprawdę nie ma znaczenia, czy metody są oznaczone, publicczy też internal.

Jedną z możliwości, które przychodzą na myśl, jest to, że klasa była publiczna, a później została zmieniona na, internala programista nie zadał sobie trudu, aby zmienić wszystkie modyfikatory dostępności metody.


1
+1 za przybicie oczywistego scenariusza „klasa publiczna stała się wewnętrznym”.
bopapa_1979

8

W niektórych przypadkach może być również tak, że typ wewnętrzny implementuje interfejs publiczny, co oznaczałoby, że wszelkie metody zdefiniowane w tym interfejsie nadal będą musiały zostać zadeklarowane jako publiczne.



0

internalmówi, że do członka można uzyskać dostęp tylko z tego samego zespołu. Inne klasy w tym zestawie mogą uzyskiwać dostęp do internal publicelementu, ale nie będą miały dostępu do elementu privatelub protectedelementu, internallub nie.


Ale gdyby metody zostały oznaczone internalzamiast public, ich widoczność nie zmieniłaby się. Uważam, że o to pyta OP.
Oded

1
@zapthedingbat - post jest poprawny pod względem faktycznym, ale czy faktycznie odpowiada na pytanie?
Oded

1
Tak, pytanie brzmi: DLACZEGO należy je tak oznaczyć. Rozumiem podstawy zakresu. Dzięki za próbę!
bopapa_1979

0

Naprawdę walczyłem z tym dzisiaj. Do tej pory powiedziałbym, że wszystkie metody powinny być naznaczone, internalgdyby klasa była internali rozważałaby coś innego po prostu złe kodowanie lub lenistwo, szczególnie w rozwoju przedsiębiorstw; musiałem jednak podklasować publicklasę i zastąpić jedną z jej metod:

internal class SslStreamEx : System.Net.Security.SslStream
{
    public override void Close()
    {
        try
        {
            // Send close_notify manually
        }
        finally
        {
            base.Close();
        }
    }
}

Metoda MUSI być publici sprawiło, że pomyślałem, że naprawdę nie ma logicznego sensu ustalania metod, internalchyba że tak naprawdę musi być, jak powiedział Eric Lippert.

Do tej pory nigdy tak naprawdę nie zastanawiałem się nad tym, po prostu to zaakceptowałem, ale po przeczytaniu posta Erica naprawdę mnie to zastanowiło i po wielu rozważaniach ma to sens.


0

Jest różnica. W naszym projekcie wprowadziliśmy wiele klas wewnętrznych, ale przeprowadzamy testy jednostkowe w innym zestawie, aw naszych informacjach o zespole użyliśmy InternalsVisibleTo, aby umożliwić zespołowi UnitTest wywoływanie klas wewnętrznych. Zauważyłem, że jeśli klasa wewnętrzna ma konstruktora wewnętrznego, z jakiegoś powodu nie jesteśmy w stanie utworzyć instancji za pomocą Activator.CreateInstance w zestawie testów jednostkowych. Ale jeśli zmienimy konstruktor na publiczny, ale klasa jest nadal wewnętrzna, działa dobrze. Ale wydaje mi się, że jest to bardzo rzadki przypadek (jak Eric powiedział w oryginalnym poście: Odbicie).


0

Myślę, że mam na ten temat dodatkową opinię. Na początku zastanawiałem się, w jaki sposób sens ogłosić coś publicznie w klasie wewnętrznej. Skończyłem tutaj, czytając, że może być dobrze, jeśli później zdecydujesz się zmienić klasę na publiczną. Prawdziwe. Tak więc w moim umyśle powstał wzór : jeśli to nie zmieni obecnego zachowania, to bądźcie tolerancyjni i dopuszczajcie rzeczy, które nie mają sensu (i nie ranią) w obecnym stanie kodu, ale później tak by było, gdybyście zmień deklarację klasy.

Lubię to:

public sealed class MyCurrentlySealedClass
{
    protected void MyCurretlyPrivateMethod()
    {
    }
}

Zgodnie z „wzorem”, o którym wspomniałem powyżej, powinno to być całkowicie w porządku. Wynika to z tego samego pomysłu. Zachowuje się jak privatemetoda, ponieważ nie można dziedziczyć klasy. Ale jeśli usuniesz sealedograniczenie, nadal będzie ważne: odziedziczone klasy zobaczą tę metodę, co absolutnie chciałem osiągnąć. Ale pojawia się ostrzeżenie: CS0628lub CA1047. Obaj nie zamierzają deklarować protectedczłonków w sealedklasie. Co więcej, doszedłem do pełnej zgody, że nie ma sensu: ostrzeżenie „Członek chroniony w zapieczętowanej klasie” (klasa singletona)

Więc po tym ostrzeżeniu i połączonej dyskusji postanowiłem uczynić wszystko wewnętrznym lub mniejszym, w klasie wewnętrznej, ponieważ bardziej odpowiada temu rodzajowi myślenia i nie mieszamy różnych „wzorów”.


Zdecydowanie wolę robić to tak (a przynajmniej z powodów), powiedział Eric Lippert. Jego artykuł jest zdecydowanie wart przeczytania.
bopapa_1979
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.