Er det interne C #-nøgleord en kodelugt?

I dette indlæg skal jeg vise, hvorfor jeg synes, at det interne søgeord, når det sættes på klassemedlemmer, er en kodelugt og foreslår bedre alternativer.

Hvad er det interne søgeord?

I C # kan det interne nøgleord bruges på en klasse eller dets medlemmer. Det er en af ​​C # adgangsmodifikatorer. Interne typer eller medlemmer er kun tilgængelige inden for filer i samme samling . (C # intern nøgleordsdokumentation).

Hvorfor har vi brug for det interne søgeord?

”En almindelig brug af intern adgang er i komponentbaseret udvikling, fordi det gør det muligt for en gruppe af komponenter at samarbejde privat uden at blive udsat for resten af ​​applikationskoden . For eksempel kunne en ramme til opbygning af grafiske brugergrænseflader give kontrol- og formklasser, der samarbejder ved at bruge medlemmer med intern adgang. Da disse medlemmer er interne, udsættes de ikke for kode, der bruger rammen. ” (C # intern søgeordsdokumentation)

Dette er de brugssager, jeg så for at bruge det interne nøgleord på et klassemedlem:

  • Ring til en klasses private funktion inden for samme samling.
  • For at teste en privat funktion kan du markere den som intern og udsætte dllen for test-DLL'en via InternalsVisibleTo.

Begge sager kan ses som en kodelugt og siger, at denne private funktion skal være offentlig.

Lad os se nogle eksempler

Her er et simpelt eksempel. en funktion i en klasse ønsker at få adgang til en privat funktion i en anden klasse.

class A{ public void func1(){ func2(); } private void func2(){} } class B{ public void func(A a){ a.func2(); //Compilation error 'A.func2()' is inaccessible due to its protection level } }

Løsningen er enkel - markér bare A :: func2 som offentlig.

Lad os se på et lidt mere komplekst eksempel:

public class A{ public void func1(){} private void func2(B b){} } internal class B{ public void func3(A a){ a.func2(this); //Compilation error 'A.func2(B)' is inaccessible due to its protection level } }

Hvad er problemet? bare markér func2 som offentlig, som vi gjorde før.

public class A{ public void func1(){ ... } public void func2(B b){ ...} // Compilation error: Inconsistent accessibility: parameter type 'B' is less accessible than method 'A.func2(B)' } internal class B{ public void func3(A a){ a.func2(this); } }

Men vi kan ikke? B er en intern klasse, så den kan ikke være en del af underskriften af ​​en offentlig funktion af en offentlig klasse.

Det er de løsninger, jeg fandt, bestilt efter lethed:

  1. Marker funktionen med det interne nøgleord
public class A{ public void func1(){ } internal void func2(B b){} } internal class B{ public void func3(A a){ a.func2(this); } }

2. Opret en intern grænseflade

internal interface IA2{ void func2(B b); } public class A:IA2{ public void func1(){ var b = new B(); b.func3(this); } void IA2.func2(B b){} //implement IA2 explicitly because func2 can't be public } internal class B{ public void func3(A a){ ((IA2)a).func2(this); //use interface instead of class to access func2 } }

3. Uddrag A.func2 til en anden intern klasse, og brug den i stedet for A.func2.

internal class C{ public void func2(B b){ //extract A:func2 to here } } public class A{ public void func1(){} private void func2(B b){ new C().func2(b); } } internal class B{ public void func3(){ //a is no longer needed new C().func2(this); //use internal class instead of private function } }

4. Afkobling af funktionen fra interne klasser og offentliggør den. Dette afhænger meget af, hvad funktionen laver med sine indgange. afkoble de interne klasser kan være meget lette, meget hårde og endda umulige (uden at ødelægge designet).

Men vi har ikke offentlige klasser, vi bruger grænseflader ...

Lad os se på et mere virkeligt eksempel:

public interface IA{ void func1(); } internal class A : IA { public void func1(){} private void func2(B b){} } internal class B{ public void func3(IA a){ a.func2(this); //Compilation error IA' does not contain a definition for 'func2' and no extension method 'func2' accepting a first argument of type 'IA' could be found } }

Lad os se, hvordan de tidligere løsninger er tilpasset dette eksempel:

  1. Marker funktion med Intern. dette betyder, at du bliver nødt til at caste til klasse for at ringe til funktionen, så dette fungerer kun, hvis klasse A er den eneste, der implementerer grænsefladen , hvilket betyder, at IA ikke bespottes i test, og der ikke er en anden produktionsklasse, der implementerer IA .
public interface IA{ void func1(); } internal class A : IA { public void func1(){} internal void func2(B b){} } internal class B{ public void func3(IA a){ ((A)a).func2(this); //cast to A in order to accses func2 } }

2. Opret en intern grænseflade, der udvider den offentlige grænseflade.

internal interface IExtendedA : IA{ void func2(B b); } public interface IA{ void func1(); } internal class A : IExtendedA { public void func1(){} public void func2(B b){} } internal class B{ public void func3(IExtendedA a){ a.func2(this); } }

3. Uddrag A.func2 til en anden intern klasse, og brug den i stedet for A.func2.

4. Afkobling af funktionen fra interne klasser og føjes til den offentlige grænseflade.

Vi kan se, at det interne nøgleord er den nemmeste løsning , men der er andre løsninger, der bruger de traditionelle byggesten i OOP: klasser og grænseflader . Vi kan se, at den 2. løsning - tilføjelse af en intern grænseflade ikke er meget sværere end at markere funktionen med det interne nøgleord.

Hvorfor ikke bruge det interne søgeord?

Som jeg viste i de foregående eksempler, er det nemmeste at bruge det interne nøgleord . Men du har det svært i fremtiden, hvis du bliver nødt til at:

  • Flyt den offentlige klasse A til en anden DLL (da det interne søgeord ikke længere gælder for den samme dll)
  • Opret en anden produktionsklasse, der implementerer IA
  • Mock IA i test

Du tænker måske ”Men dette er bare en linje kode, jeg eller nogen anden kan ændre det let, hvis det er nødvendigt”. Nu har du en linje kode, der ser sådan ud:

((MyClass)a).internalFunction

men hvis andre også skal ringe til denne funktion, kopieres denne linje rundt i DLL-filen.

Min konklusion

Jeg synes, at markere et klassemedlem med det interne nøgleord er en kodelugt . I eksemplerne, jeg har vist ovenfor, er det den nemmeste løsning, MEN kan forårsage problemer i fremtiden. Oprettelse af en intern grænseflade er næsten lige så let og mere eksplicit.

Sammenlign med C ++

Nøgleordet C ++ "ven" svarer til det interne C #-nøgleord. Det giver en klasse eller en funktion adgang til private medlemmer af en klasse. Forskellen er, at det giver adgang til specifik klasse eller funktion og ikke alle klasser i samme DLL. Efter min mening er dette en bedre løsning end C # internt nøgleord.

Yderligere læsning

Praktiske anvendelser af det "interne" søgeord i C #

Hvorfor giver C # ikke søgeordet 'ven' i C ++ -stil?