Czy możesz wyjaśnić, jakie jest praktyczne zastosowanie internal
słowa kluczowego w języku C #?
Wiem, że internal
modyfikator ogranicza dostęp do bieżącego zestawu, ale kiedy iw jakich okolicznościach powinienem go użyć?
Czy możesz wyjaśnić, jakie jest praktyczne zastosowanie internal
słowa kluczowego w języku C #?
Wiem, że internal
modyfikator ogranicza dostęp do bieżącego zestawu, ale kiedy iw jakich okolicznościach powinienem go użyć?
Odpowiedzi:
Klasy / metody pomocnicze lub pomocnicze, do których chcesz uzyskać dostęp z wielu innych klas w tym samym zestawie, ale które chcesz mieć pewność, że kod w innych zestawach nie będzie miał dostępu.
Z MSDN (przez archive.org):
Powszechnym zastosowaniem dostępu wewnętrznego jest opracowywanie oparte na komponentach, ponieważ umożliwia grupie komponentów prywatną współpracę bez narażania się na resztę kodu aplikacji. Na przykład struktura do tworzenia graficznych interfejsów użytkownika może zapewniać klasy Control i Form, które współpracują przy użyciu elementów z dostępem wewnętrznym. Ponieważ te elementy są wewnętrzne, nie są narażone na kod korzystający z frameworka.
Możesz również użyć modyfikatora wewnętrznego wraz z InternalsVisibleTo
atrybutem poziomu zestawu, aby utworzyć zestawy „przyjacielskie”, które mają specjalny dostęp do wewnętrznych klas zestawu docelowego.
Może to być przydatne do tworzenia zespołów testujących jednostki, które następnie mogą wywoływać wewnętrzne elementy zespołu do testowania. Oczywiście żaden inny zestaw nie ma takiego poziomu dostępu, więc po zwolnieniu systemu hermetyzacja jest utrzymywana.
Jeśli Bob potrzebuje BigImportantClass, wtedy Bob musi przekonać ludzi, którzy posiadają projekt A, do zarejestrowania się, aby zagwarantować, że BigImportantClass zostanie napisany, aby zaspokoić jego potrzeby, przetestowany pod kątem spełnienia jego potrzeb, udokumentowany jako spełniający jego potrzeby i proces zostanie wprowadzony, aby zapewnić, że nigdy nie zostanie zmieniony, aby nie spełniał już jego potrzeb.
Jeśli klasa jest wewnętrzna, to nie musi przechodzić przez ten proces, co oszczędza budżet na Projekt A, który mogą wydać na inne rzeczy.
Nie chodzi o to, że wewnętrzne utrudnia życie Boba. Dzięki temu możesz kontrolować, jakie drogie obietnice składa projekt A pod względem funkcji, żywotności, kompatybilności i tak dalej.
Innym powodem użycia wewnętrznego jest zaciemnienie plików binarnych. Obfuscator wie, że szyfrowanie nazw klas dowolnych klas wewnętrznych jest bezpieczne, podczas gdy nazw klas publicznych nie można mieszać, ponieważ mogłoby to zniszczyć istniejące odniesienia.
Jeśli piszesz bibliotekę DLL, która zawiera tonę złożonej funkcjonalności w prostym publicznym interfejsie API, wówczas „wewnętrzny” jest używany dla członków klasy, których nie należy ujawniać publicznie.
Ukrywanie złożoności (inaczej enkapsulacja) to główna koncepcja inżynierii oprogramowania wysokiej jakości.
Wewnętrzne słowo kluczowe jest szeroko stosowane, gdy budujesz opakowanie na niezarządzanym kodzie.
Gdy masz bibliotekę opartą na C / C ++, którą chcesz DllImport, możesz zaimportować te funkcje jako funkcje statyczne klasy i uczynić je wewnętrznymi, aby użytkownik miał dostęp tylko do opakowania, a nie oryginalnego interfejsu API, więc nie może zadzierać z czymkolwiek. Funkcje są statyczne, można ich używać wszędzie w zespole, dla wielu potrzebnych klas opakowań.
Możesz rzucić okiem na Mono.Cairo, jest to opakowanie wokół biblioteki Cairo, która wykorzystuje to podejście.
Kierując się zasadą „używaj możliwie ścisłego modyfikatora”, używam wszędzie tam, gdzie potrzebuję dostępu, powiedzmy, metody z innej klasy, dopóki nie będę musiał uzyskać do niej dostępu z innego zestawu.
Ponieważ interfejs asemblera jest zwykle węższy niż suma interfejsów jego klas, używam go w wielu miejscach.
Uważam, że wewnętrzne jest zdecydowanie zbyt często wykorzystywane. naprawdę nie powinieneś wystawiać pewnych funkcji tylko na pewne klasy, których nie zrobiłbyś innym konsumentom.
To, moim zdaniem, psuje interfejs, psuje abstrakcję. Nie oznacza to, że nigdy nie należy go używać, ale lepszym rozwiązaniem jest refaktoryzacja do innej klasy lub użycie, jeśli to możliwe, w inny sposób. Jednak nie zawsze jest to możliwe.
Przyczyną problemów może być to, że inny programista może zostać obciążony budowaniem innej klasy w tym samym zestawie, co twój. Posiadanie elementów wewnętrznych zmniejsza jasność abstrakcji i może powodować problemy, jeśli zostanie niewłaściwie użyte. Byłby to ten sam problem, jakbyś go upublicznił. Druga klasa budowana przez innego programistę jest nadal konsumentem, tak jak każda klasa zewnętrzna. Abstrakcja i enkapsulacja klas nie służy tylko ochronie dla / przed klasami zewnętrznymi, ale dla każdej i wszystkich klas.
Innym problemem jest to, że wielu programistów pomyśli, że mogą potrzebować użyć go gdzie indziej w asemblerze i oznaczyć go jako wewnętrzny, mimo że w danym momencie tego nie potrzebują. Inny deweloper może więc pomyśleć, że jest tam do wzięcia. Zazwyczaj chcesz oznaczyć jako prywatny, dopóki nie będziesz mieć ostatecznej potrzeby.
Ale niektóre z nich mogą być subiektywne i nie twierdzę, że nigdy nie należy ich używać. Po prostu użyj w razie potrzeby.
Ciekawego widziałem kiedyś, może tydzień, na blogu, którego nie pamiętam. Zasadniczo nie mogę tego przypisać, ale pomyślałem, że może mieć jakąś przydatną aplikację.
Powiedzmy, że chciałeś, aby klasa abstrakcyjna była widoczna dla innego zestawu, ale nie chcesz, aby ktoś mógł po niej dziedziczyć. Sealed nie będzie działać, ponieważ jest z jakiegoś powodu abstrakcyjny, inne klasy w tym zestawie dziedziczą po nim. Prywatne nie będzie działać, ponieważ możesz chcieć zadeklarować klasę nadrzędną gdzieś w innym zestawie.
przestrzeń nazw Base.Assembly { publiczna klasa abstrakcyjna Rodzic { wewnętrzne streszczenie void SomeMethod (); } // Działa to dobrze, ponieważ jest w tym samym zestawie. klasa publiczna ChildWithin: Parent { przesłonięcie wewnętrzne void SomeMethod () { } } } przestrzeń nazw Another.Assembly { // Kaboom, ponieważ nie można przesłonić metody wewnętrznej klasa publiczna ChildOutside: Parent { } test klasy publicznej { //W porządku prywatny rodzic _parent; test publiczny () { //Nadal dobrze _parent = new ChildWithin (); } } }
Jak widać, skutecznie pozwala komuś korzystać z klasy Parent bez możliwości dziedziczenia.
Ten przykład zawiera dwa pliki: Assembly1.cs i Assembly2.cs. Pierwszy plik zawiera wewnętrzną klasę bazową BaseClass. W drugim pliku próba utworzenia instancji BaseClass spowoduje błąd.
// Assembly1.cs
// compile with: /target:library
internal class BaseClass
{
public static int intM = 0;
}
// Assembly1_a.cs
// compile with: /reference:Assembly1.dll
class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // CS0122
}
}
W tym przykładzie użyj tych samych plików, których użyto w przykładzie 1, i zmień poziom dostępności BaseClass na publiczny . Zmień także poziom dostępności elementu IntM na wewnętrzny . W takim przypadku można utworzyć instancję klasy, ale nie można uzyskać dostępu do elementu wewnętrznego.
// Assembly2.cs
// compile with: /target:library
public class BaseClass
{
internal static int intM = 0;
}
// Assembly2_a.cs
// compile with: /reference:Assembly1.dll
public class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // Ok.
BaseClass.intM = 444; // CS0117
}
}
źródło : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx
Gdy masz metody, klasy itp., Które muszą być dostępne w ramach bieżącego zestawu, a nigdy poza nim.
Na przykład DAL może mieć ORM, ale obiekty nie powinny być narażone na warstwę biznesową, wszystkie interakcje powinny być wykonywane metodami statycznymi i przekazywanie wymaganych parametrów.
Bardzo interesującym zastosowaniem wewnętrznego - z wewnętrznym elementem ograniczonym oczywiście tylko do zestawu, w którym jest zadeklarowany - jest do pewnego stopnia uzyskanie funkcji „przyjaciela”. Członek-przyjaciel jest czymś, co jest widoczne tylko dla niektórych innych zespołów poza zespołem, w którym zostało zadeklarowane. C # nie ma wbudowanego wsparcia dla przyjaciela, jednak CLR tak.
Możesz użyć InternalsVisibleToAttribute do zadeklarowania zestawu znajomych, a wszystkie odniesienia z zestawu znajomych będą traktować wewnętrznych członków twojego deklarującego zestawu jako publiczne w zakresie zestawu znajomych. Problem polega na tym, że wszystkie elementy wewnętrzne są widoczne; nie możesz wybrać.
Dobrym zastosowaniem dla InternalsVisibleTo jest wystawienie różnych elementów wewnętrznych na zespół testowy jednostki, eliminując w ten sposób potrzebę skomplikowanych prac związanych z odbiciem w celu przetestowania tych elementów. Wszyscy członkowie wewnętrzni, którzy są widoczni, nie stanowią większego problemu, jednak takie podejście dość mocno psuje interfejsy klas i może potencjalnie zrujnować enkapsulację wewnątrz deklarującego zestawu.
Zasadniczo istnieją dwa rodzaje członków:
Redukcja szumów, im mniej typów ujawniasz, tym prostsza jest twoja biblioteka. Zabezpieczenie przed manipulacją / Zabezpieczenie to kolejne (choć Odbicie może wygrać z nim).
Klasy wewnętrzne umożliwiają ograniczenie interfejsu API zestawu. Ma to zalety, takie jak uproszczenie interfejsu API.
Ponadto, jeśli błąd występuje w twoim zestawie, istnieje mniejsze prawdopodobieństwo, że poprawka wprowadzi przełomową zmianę. Bez klas wewnętrznych należałoby założyć, że zmiana członków publicznych dowolnej klasy byłaby przełomową zmianą. W przypadku klas wewnętrznych można założyć, że modyfikowanie ich publicznych elementów tylko psuje wewnętrzny interfejs API zestawu (i wszelkich zestawów, do których odwołuje się atrybut InternalsVisibleTo).
Lubię mieć enkapsulację na poziomie klasy i na poziomie asemblera. Niektórzy nie zgadzają się z tym, ale miło jest wiedzieć, że ta funkcjonalność jest dostępna.
Mam projekt, który używa LINQ-SQL dla zaplecza danych. Mam dwie główne przestrzenie nazw: Biz i Data. Model danych LINQ żyje w danych i jest oznaczony jako „wewnętrzny”; przestrzeń nazw Biz ma klasy publiczne, które otaczają klasy danych LINQ.
Więc jest Data.Client
i Biz.Client
; ten ostatni ujawnia wszystkie istotne właściwości obiektu danych, np .:
private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }
Obiekty Biz mają prywatnego konstruktora (aby wymusić użycie metod fabrycznych) i wewnętrznego konstruktora, który wygląda następująco:
internal Client(Data.Client client) {
this._client = client;
}
Może to być wykorzystane przez dowolną klasę biznesową w bibliotece, ale interfejs użytkownika nie ma bezpośredniego dostępu do modelu danych, zapewniając, że warstwa biznesowa zawsze działa jako pośrednik.
To pierwszy raz, kiedy naprawdę internal
dużo zużyłem i okazuje się to całkiem przydatne.
Są przypadki, w których sensowne jest tworzenie członków klas internal
. Jednym z przykładów może być kontrolowanie sposobu tworzenia instancji klas; powiedzmy, że udostępniasz jakąś fabrykę do tworzenia instancji klasy. Możesz utworzyć konstruktor internal
, aby fabryka (która znajduje się w tym samym zestawie) mogła tworzyć instancje klasy, ale kod poza tym zestawem nie może.
Jednak nie widzę sensu w tworzeniu klas lub członków internal
bez konkretnych powodów, tak mało, jak sensowne jest ich tworzenie public
, lub private
bez konkretnych powodów.
jedyną rzeczą, w której kiedykolwiek użyłem wewnętrznego słowa kluczowego, jest kod sprawdzający licencję w moim produkcie ;-)
Jednym z zastosowań wewnętrznego słowa kluczowego jest ograniczenie dostępu do konkretnych implementacji przez użytkownika twojego zestawu.
Jeśli masz fabrykę lub inną centralną lokalizację do konstruowania obiektów, użytkownik twojego zestawu musi mieć do czynienia tylko z interfejsem publicznym lub abstrakcyjną klasą bazową.
Ponadto konstruktory wewnętrzne pozwalają kontrolować, gdzie i kiedy tworzy się instancję klasy publicznej.
Co powiesz na to: zazwyczaj zaleca się, aby nie wystawiać obiektu List zewnętrznym użytkownikom zestawu, a raczej wystawiać IEnumerable. Ale o wiele łatwiej jest używać obiektu List wewnątrz zestawu, ponieważ otrzymujesz składnię tablic i wszystkie inne metody List. Tak więc zazwyczaj mam wewnętrzną właściwość odsłaniającą Listę do użycia wewnątrz zestawu.
Mile widziane są komentarze dotyczące tego podejścia.
Pamiętaj, że każda klasa zdefiniowana jako public
automatycznie pojawi się w inteligencji, gdy ktoś spojrzy na przestrzeń nazw twojego projektu. Z punktu widzenia interfejsu API ważne jest, aby pokazywać użytkownikom projektu tylko te klasy, których mogą używać. Użyj internal
słowa kluczowego, aby ukryć rzeczy, których nie powinny widzieć.
Jeśli twój Big_Important_Class
projekt A jest przeznaczony do użytku poza projektem, nie powinieneś go oznaczać internal
.
Jednak w wielu projektach często masz zajęcia, które naprawdę są przeznaczone tylko do użytku w projekcie. Na przykład może istnieć klasa przechowująca argumenty do sparametryzowanego wywołania wątku. W takich przypadkach należy oznaczyć je tak, internal
jakby nie z innego powodu niż w celu ochrony przed niezamierzoną zmianą interfejsu API w późniejszym okresie.
private
kluczowego można używać tylko w przypadku klas lub struktur zdefiniowanych w innej klasie lub strukturze. Klasa zdefiniowana w przestrzeni nazw może być tylko zadeklarowana public
lub internal
.
Chodzi o to, że kiedy projektujesz bibliotekę, tylko klasy przeznaczone do użytku z zewnątrz (przez klientów twojej biblioteki) powinny być publiczne. W ten sposób możesz ukryć klasy, które
itp.
Jeśli opracowujesz rozwiązania wewnętrzne, to użycie elementów wewnętrznych nie jest tak ważne, ponieważ zwykle klienci będą mieli stały kontakt z tobą i / lub dostęp do kodu. Są jednak dość krytyczne dla twórców bibliotek.
Gdy masz klasy lub metody, które nie pasują idealnie do paradygmatu zorientowanego obiektowo, które robią niebezpieczne rzeczy, które trzeba wywoływać z innych klas i metod pod kontrolą, i których nie chcesz pozwalać innym .
public class DangerousClass {
public void SafeMethod() { }
internal void UpdateGlobalStateInSomeBizarreWay() { }
}