Je interné kľúčové slovo C # kódovým pachom?

V tomto príspevku ukážem, prečo si myslím, že keď je kľúčové slovo internal vložené do členov triedy, je vôňou kódu a navrhuje lepšie alternatívy.

Čo je interné kľúčové slovo?

V C # možno interné kľúčové slovo použiť na triedu alebo jej členov. Je to jeden z modifikátorov prístupu C #. Interné typy alebo členovia sú prístupné iba v rámci súborov v tej istej zostave . (C # interná dokumentácia kľúčových slov).

Prečo potrebujeme interné kľúčové slovo?

„Bežné použitie interného prístupu je vo vývoji komponentov, pretože umožňuje skupine komponentov spolupracovať súkromným spôsobom bez toho, aby boli vystavení zvyšku kódu aplikácie . Napríklad rámec pre vytváranie grafických používateľských rozhraní by mohol poskytovať triedy Control a Form, ktoré spolupracujú pomocou členov s interným prístupom. Pretože sú títo členovia interní, nie sú vystavení kódu, ktorý používa framework. “ (C # interná dokumentácia kľúčových slov)

Toto sú prípady použitia, ktoré som videl pri použití interného kľúčového slova pre člena triedy:

  • Zavolajte súkromnú funkciu triedy v rámci tej istej zostavy.
  • Ak chcete otestovať súkromnú funkciu, môžete ju označiť ako internú a vystaviť ju dll testovacej DLL prostredníctvom InternalsVisibleTo.

Na obidva prípady sa dá pozerať ako na zápach kódu, podľa ktorého by táto súkromná funkcia mala byť verejná.

Pozrime sa na niekoľko príkladov

Tu je jednoduchý príklad. funkcia v jednej triede chce získať prístup k súkromnej funkcii inej triedy.

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 } }

Riešenie je jednoduché - stačí označiť A :: func2 ako verejné.

Pozrime sa na trochu zložitejší príklad:

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 } }

Aký je problém? stačí označiť func2 ako verejný ako predtým.

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); } }

Ale nemôžeme? B je interná trieda, takže nemôže byť súčasťou podpisu verejnej funkcie verejnej triedy.

Toto sú riešenia, ktoré som našiel, zoradené podľa ľahkosti:

  1. Označte funkciu interným kľúčovým slovom
public class A{ public void func1(){ } internal void func2(B b){} } internal class B{ public void func3(A a){ a.func2(this); } }

2. Vytvorte interné rozhranie

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. Extrahujte A.func2 do inej internej triedy a použite ju namiesto 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. Oddeľte funkciu od interných tried a zverejnite ju. To veľmi závisí od toho, čo funkcia robí so svojimi vstupmi. oddelenie vnútorných tried môže byť veľmi ľahké, veľmi tvrdé a dokonca nemožné (bez narušenia vzhľadu).

Ale nemáme verejné triedy, používame rozhrania ...

Pozrime sa na ďalší príklad z reálneho sveta:

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 } }

Pozrime sa, ako sú predchádzajúce riešenia prispôsobené tomuto príkladu:

  1. Označiť funkciu ako internú. to znamená, že na to, aby ste mohli funkciu volať, budete musieť cast do triedy. Toto bude fungovať, iba ak trieda A bude jediná, ktorá implementuje rozhranie , čo znamená, že IA sa v testoch neposmieva a neexistuje iná produkčná trieda, ktorá implementuje 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. Vytvorte interné rozhranie, ktoré rozširuje verejné rozhranie.

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. Extrahujte A.func2 do inej internej triedy a použite ju namiesto A.func2.

4. Oddeľte funkciu od interných tried a pridajte ju do verejného rozhrania.

Vidíme, že interné kľúčové slovo je najjednoduchšie riešenie , ale existujú aj ďalšie riešenia využívajúce tradičné stavebné prvky OOP: triedy a rozhrania . Vidíme, že 2. riešenie - pridanie interného rozhrania nie je oveľa ťažšie ako označenie funkcie interným kľúčovým slovom.

Prečo nepoužívať interné kľúčové slovo?

Ako som ukázal v predchádzajúcich príkladoch, použitie interného kľúčového slova je najjednoduchšie riešenie . V budúcnosti to však budete mať ťažké, ak budete potrebovať:

  • Presuňte verejnú triedu A do inej DLL (pretože interné kľúčové slovo sa už nebude vzťahovať na tú istú dll)
  • Vytvorte ďalšiu produkčnú triedu, ktorá implementuje IA
  • Mock IA v testoch

Možno si myslíte, že „Toto je však iba jeden riadok kódu, ktorý môžem v prípade potreby ľahko zmeniť ja alebo ktokoľvek iný.“ Teraz máte jeden riadok kódu, ktorý vyzerá takto:

((MyClass)a).internalFunction

ale ak budú musieť túto funkciu zavolať aj ostatní, tento riadok sa prekopíruje okolo vo vnútri DLL.

Môj záver

Myslím, že označenie člena triedy pomocou interného kľúčového slova je vôňa kódu . V príkladoch, ktoré som ukázal vyššie, je to najjednoduchšie riešenie, ALE to môže v budúcnosti spôsobiť problémy. Vytvorenie interného rozhrania je takmer rovnako ľahké a jasnejšie.

Porovnaj s C ++

Kľúčové slovo „friend“ v C ++ je podobné ako interné kľúčové slovo C #. Umožňuje triede alebo funkcii prístup k súkromným členom triedy. Rozdiel je v tom, že umožňuje prístup k konkrétnej triede alebo funkcii a nie ku všetkým triedam v tej istej knižnici DLL. Podľa môjho názoru je to lepšie riešenie ako interné kľúčové slovo C #.

Ďalšie čítanie

Praktické použitie „interného“ kľúčového slova v C #

Prečo C # neposkytuje kľúčové slovo „friend“ v štýle C ++?