Да ли је интерна кључна реч Ц # мирис мириса?

У овом посту ћу показати зашто мислим да је унутрашња кључна реч, када се стави на чланове разреда, мирис кода и предлажем боље алтернативе.

Шта је интерна кључна реч?

У Ц # се интерна кључна реч може користити за класу или њене чланове. То је један од модификатора приступа Ц #. Интерни типови или чланови су доступни само унутар датотека у истом склопу . (Интерна документација о кључним речима Ц #).

Зашто нам је потребна интерна кључна реч?

„Уобичајена употреба интерног приступа је у развоју заснованом на компонентама, јер омогућава групи компонената да сарађују на приватни начин, а да нису изложени остатку апликационог кода . На пример, оквир за изградњу графичког корисничког интерфејса могао би да обезбеди класе Цонтрол и Форм које сарађују коришћењем чланова са унутрашњим приступом. Будући да су ови чланови интерни, нису изложени коду који користи оквир. “ (Интерна документација о кључним речима Ц #)

Ово су примери употребе које сам видео за употребу интерне кључне речи на члану класе:

  • Позовите приватну функцију класе у оквиру истог склопа.
  • Да бисте тестирали приватну функцију, можете је означити као интерну и изложити длл тестном ДЛЛ-у путем ИнтерналсВисиблеТо.

Оба случаја могу се посматрати као мирис кода, говорећи да би ова приватна функција требала бити јавна.

Погледајмо неколико примера

Ево једноставног примера. функција у једној класи жели да приступи приватној функцији друге класе.

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

Решење је једноставно - само означите А :: фунц2 као јавни.

Погледајмо мало сложенији пример:

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

У чему је проблем? само означите фунц2 као јавни као и пре.

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

Али не можемо? Б је интерна класа, тако да не може бити део потписа јавне функције јавне класе.

То су решења која сам пронашао, поредана по лакоћи:

  1. Означите функцију интерном кључном речи
public class A{ public void func1(){ } internal void func2(B b){} } internal class B{ public void func3(A a){ a.func2(this); } }

2. Креирајте интерни интерфејс

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. Издвојите А.фунц2 у другу интерну класу и користите је уместо А.фунц2.

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. Одвојите функцију од интерних часова и учините је јавном. Ово много зависи од тога шта функција ради са својим улазима. раздвојити интерне класе може бити врло лако, веома тешко и чак немогуће (без нарушавања дизајна).

Али ми немамо јавне класе, користимо интерфејсе ...

Погледајмо још неколико примера из стварног света:

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

Погледајмо како су претходна решења прилагођена овом примеру:

  1. Означи функцију са Интернал. то значи да ћете морати да пребаците у класу да бисте позвали функцију, па ће ово функционисати само ако је класа А једина која имплементира интерфејс , што значи да се ИА не изругује у тестовима и не постоји друга продукцијска класа која имплементира ИА .
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. Креирајте интерни интерфејс који проширује јавни интерфејс.

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. Издвојите А.фунц2 у другу интерну класу и користите је уместо А.фунц2.

4. Одвојите функцију од интерних класа и додајте је у јавни интерфејс.

Видимо да је интерна кључна реч најлакше решење , али постоје и друга решења која користе традиционалне грађевне блокове ООП-а: класе и интерфејси . Видимо да друго решење - додавање интерног интерфејса није много теже од обележавања функције интерном кључном речи.

Зашто не користити интерну кључну реч?

Као што сам показао у претходним примерима, коришћење интерне кључне речи је најлакше решење . Али убудуће ће вам бити тешко ако будете морали:

  • Преместите јавну класу А у другу ДЛЛ датотеку (јер се унутрашња кључна реч више неће примењивати на исти длл)
  • Направите још једну производну класу која примењује ИА
  • Исмевајте ИА у тестовима

Можете помислити „Али ово је само један ред кода, ја или било ко други може га лако променити ако је потребно“. Сада имате један ред кода који изгледа тако:

((MyClass)a).internalFunction

али ако ће и други требати да позову ову функцију, ова линија ће се копирати унутар ДЛЛ-а.

Мој закључак

Мислим да је обележавање члана разреда унутрашњом кључном речи мирис кода . У примерима које сам горе показао то је најлакше решење, АЛИ у будућности може стварати проблеме. Стварање интерног интерфејса је готово једнако лако и експлицитније.

Упоредите са Ц ++

Кључна реч Ц ++ „пријатељ“ слична је интерној кључној речи Ц #. Омогућава класи или функцији приступ приватним члановима класе. Разлика је у томе што омогућава приступ одређеној класи или функцији, а не свим класама у истом ДЛЛ-у. По мом мишљењу, ово је боље решење од интерне кључне речи Ц #.

Додатна литература

Практична употреба „интерне“ кључне речи у Ц #

Зашто Ц # не пружа кључну реч „пријатељ“ у стилу Ц ++?