Kas C # sisemine märksõna on koodilõhn?

Selles postituses näitan, miks arvan, et sisemine märksõna klassi liikmetele pannes on koodilõhn ja pakun välja paremaid alternatiive.

Mis on sisemine märksõna?

C # keeles saab sisemist märksõna kasutada klassis või selle liikmetel. See on üks C # juurdepääsu modifikaatoritest. Sisemistele tüüpidele või liikmetele on juurdepääs ainult sama kooste failides . (C # sisemine märksõna dokumentatsioon).

Miks me vajame sisemist märksõna?

„Sisemise juurdepääsu tavaline kasutamine on komponentipõhine arendus, sest see võimaldab komponentide rühmal teha privaatset koostööd, ilma et ülejäänud rakenduskood kokku puutuks . Näiteks võiks graafiliste kasutajaliideste loomise raamistik pakkuda juhtimis- ja vormiklassid, mis teevad koostööd, kasutades sisemise juurdepääsuga liikmeid. Kuna need liikmed on sisemised, ei puutu nad kokku raamistikku kasutava koodiga. " (C # sisemine märksõna dokumentatsioon)

Need on juhtumid, mida nägin sisemise märksõna kasutamisel klassi liikmel:

  • Kutsuge klassi privaatset funktsiooni samas koosseisus.
  • Erafunktsiooni testimiseks võite selle märkida sisemiseks ja paljastada DLL DLL-i kaudu InternalsVisibleTo kaudu.

Mõlemat juhtumit võib vaadelda koodilõhnana, öeldes, et see privaatne funktsioon peaks olema avalik.

Vaatame mõningaid näiteid

Siin on lihtne näide. ühe klassi funktsioon soovib pääseda juurde teise klassi privaatsele funktsioonile.

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

Lahendus on lihtne - märkige lihtsalt A :: func2 avalikuks.

Vaatame natuke keerulisemat näidet:

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

Mis on probleemiks? lihtsalt märgi func2 avalikuks nagu me seda varem tegime.

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

Aga me ei saa? B on siseklass, seega ei saa see olla osa avaliku klassi avaliku funktsiooni allkirjast.

Need on minu leitud lahendused, mis on järjestatud kerguse järgi:

  1. Märkige funktsioon sisemise märksõnaga
public class A{ public void func1(){ } internal void func2(B b){} } internal class B{ public void func3(A a){ a.func2(this); } }

2. Looge sisemine liides

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. Eemaldage A.func2 teise siseklassi ja kasutage seda A.func2 asemel.

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. Lahutage funktsioon siseklassidest ja muutke see avalikuks. See sõltub palju sellest, mida funktsioon oma sisenditega teeb. siseklasside lahutamine võib olla väga lihtne, väga raske ja isegi võimatu (ilma disaini rikkumata).

Kuid meil pole avalikke klasse, me kasutame liideseid ...

Vaatame mõnda reaalsemat näidet:

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

Vaatame, kuidas eelmised lahendused on selle näite jaoks kohandatud:

  1. Funktsiooni märkimine sisemise abil. see tähendab, et funktsiooni kutsumiseks peate klassi viima, nii et see töötab ainult siis, kui liides on ainus, mis kasutab klassi A , see tähendab, et IA-d testides ei mõnitata ja pole ühtegi teist IA-d rakendavat tootmisklassi .
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. Looge siseliides, mis laiendab avalikku liidest.

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. Eemaldage A.func2 teise siseklassi ja kasutage seda A.func2 asemel.

4. Lahutage funktsioon siseklassidest ja lisage see avalikku liidesesse.

Näeme, et sisemine märksõna on kõige lihtsam lahendus , kuid OOP traditsioonilisi ehitusplokke kasutades on ka teisi lahendusi : klassid ja liidesed . Näeme, et 2. lahendus - sisemise liidese lisamine pole palju raskem kui funktsiooni märkimine sisemise märksõnaga.

Miks mitte kasutada sisemist märksõna?

Nagu eelmistes näidetes näitasin, on sisemise märksõna kasutamine kõige lihtsam lahendus . Kuid teil on tulevikus raske, kui peate:

  • Teisaldage avalik klass A teise DLL-i (kuna sisemine märksõna ei kehti enam sama dll-i jaoks)
  • Looge veel üks IA rakendav tootmisklass
  • Pange IA proovides proovile

Võite mõelda: "Kuid see on ainult üks koodirida, mina või keegi teine ​​saab seda vajadusel lihtsalt muuta". Nüüd on teil üks koodirida, mis näeb välja selline:

((MyClass)a).internalFunction

kuid kui teised peavad ka seda funktsiooni kutsuma, kleebitakse see rida DLL -i ümber .

Minu järeldus

Ma arvan, et klassi liikme märkimine sisemise märksõnaga on koodilõhn . Ülal näidatud näidetes on see kõige lihtsam lahendus, AGA võib tulevikus probleeme tekitada. Sisemise liidese loomine on peaaegu sama lihtne ja selgem.

Võrdle C ++ -ga

Märksõna C ++ “sõber” sarnaneb C # sisemise märksõnaga. See võimaldab klassil või funktsioonil juurdepääsu klassi eraliikmetele. Erinevus on see, et see võimaldab juurdepääsu konkreetsele klassile või funktsioonile, mitte kõigile sama DLL-i klassidele. Minu arvates on see parem lahendus kui C # sisemine märksõna.

Lisalugemist

C # märksõna "sisemine" praktiline kasutus

Miks C # ei anna C ++ stiilis sõbra märksõna?