Чи можете ви поясніть, що таке практичне використання internal
ключового слова в C #?
Я знаю, що internal
модифікатор обмежує доступ до поточної збірки, але коли і за яких обставин я повинен її використовувати?
Чи можете ви поясніть, що таке практичне використання internal
ключового слова в C #?
Я знаю, що internal
модифікатор обмежує доступ до поточної збірки, але коли і за яких обставин я повинен її використовувати?
Відповіді:
Класи / методи утиліти чи помічники, до яких ви хочете отримати доступ з багатьох інших класів в межах однієї збірки, але до яких ви хочете переконатися, що код в інших збірках не може отримати доступ.
Від MSDN (через archive.org):
Загальне використання внутрішнього доступу використовується в розробці на основі компонентів, оскільки це дозволяє групі компонентів співпрацювати приватним чином, не піддаючись решті коду програми. Наприклад, рамка для побудови графічних інтерфейсів користувача може забезпечувати класи Control та Form, які співпрацюють, використовуючи членів з внутрішнім доступом. Оскільки ці учасники є внутрішніми, вони не піддаються дії коду, який використовує фреймворк.
Ви також можете використовувати внутрішній модифікатор разом з InternalsVisibleTo
атрибутом рівня складання для створення "дружніх" збірок, яким надається спеціальний доступ до внутрішніх класів цільової збірки.
Це може бути корисно для створення одиниць тестування одиниць, яким потім дозволяється викликати внутрішні члени збірки на тестування. Звичайно, жодним іншим зборам цього рівня доступу не надається, тому при випуску системи зберігається інкапсуляція.
Якщо Боб потребує BigImportantClass, тоді Боб повинен змусити людей, які мають власний проект A, щоб підписатись, щоб гарантувати, що BigImportantClass буде написаний для задоволення його потреб, перевірений на те, щоб він відповідав його потребам, був задокументований як задоволення його потреб і що процес буде запроваджено для того, щоб він ніколи не був змінений, щоб більше не відповідати його потребам.
Якщо клас є внутрішнім, він не повинен пройти цей процес, що дозволяє економити бюджет проекту A, який вони можуть витратити на інші речі.
Сенс внутрішнього не в тому, що це ускладнює життя Бобу. Це те, що дозволяє контролювати, які дорогі обіцянки дає проект A щодо функцій, терміну експлуатації, сумісності тощо.
Ще одна причина використання внутрішніх даних - якщо ви придушили свої бінарні файли. Обфускатор знає, що можна зашифрувати назву класу будь-яких внутрішніх класів, тоді як назву публічних класів неможливо скремтувати, оскільки це може зламати існуючі посилання.
Якщо ви пишете DLL, який інкапсулює тону складних функціональних можливостей у звичайний загальнодоступний API, то для членів класу, які не піддаються публічному впливу, використовується "внутрішній".
Приховування складності (також інкапсуляція) - головна концепція інженерії якості програмного забезпечення.
Внутрішнє ключове слово широко використовується, коли ви будуєте обгортку над некерованим кодом.
Якщо у вас є бібліотека на основі C / C ++, яку ви хочете DllImport, ви можете імпортувати ці функції як статичні функції класу, і зробити їх внутрішніми, тому ваш користувач має доступ лише до вашої обгортки, а не до оригінального API, тому він не може возитися ні з чим. Функції, які є статичними, ви можете використовувати їх скрізь у складі, для потрібних кількох класів обгортки.
Ви можете поглянути на Mono.Cairo, це обгортка навколо бібліотеки в Каїрі, яка використовує такий підхід.
Мене керує правило "використовувати як жорсткий модифікатор, як ви можете", я використовую внутрішню всюди, де мені потрібно отримати доступ до, скажімо, методу з іншого класу, поки я явно не потребую доступу до нього з іншої збірки.
Оскільки інтерфейс складання зазвичай вужчий, ніж сума його інтерфейсів класів, я використовую досить багато місць.
Я вважаю, що внутрішні дуже сильно використовуються. ви дійсно не повинні піддавати певній функціональності лише певних класів, яких ви не хотіли б перед іншими споживачами.
Це, на мій погляд, порушує інтерфейс, порушує абстракцію. Це не означає, що його ніколи не слід використовувати, але кращим рішенням є рефакторинг на інший клас або використання, якщо це можливо, по-іншому. Однак це може бути не завжди можливим.
Причини, які можуть викликати проблеми, полягають у тому, що іншому розробнику може бути доручено створити інший клас у тій же збірці, що і ваша. Наявність внутрішніх даних зменшує чіткість абстракції і може спричинити проблеми при неправильному використанні. Це було б те саме питання, як якщо б ви оприлюднили його. Інший клас, який будує інший розробник, все ще є споживачем, як і будь-який зовнішній клас. Абстракція класів та інкапсуляція - це не лише захист для / від зовнішніх класів, але для будь-яких класів.
Інша проблема полягає в тому, що багато розробників подумають, що їм може знадобитися використовувати його в іншому місці в зборах і позначати його як внутрішній все одно, хоча вони цього і не потребують. Тоді інший розробник може подумати, що там є для прийняття. Зазвичай ви хочете позначати приватне, поки у вас не з’явиться визначальна потреба.
Але дещо з цього може бути суб'єктивним, і я не кажу, що його ніколи не слід використовувати. Просто використовуйте, коли потрібно.
Днями, можливо, тижня, побачив цікаве в блозі, якого я не пам'ятаю. В основному я не можу взяти на це кредит, але я подумав, що це може бути корисною програмою.
Скажіть, ви хотіли, щоб абстрактний клас бачив інша збірка, але ви не хочете, щоб хтось міг успадкувати його. Запечатаний не буде працювати, тому що він абстрактний чомусь, інші класи в цій збірці успадковують його. Приватне не буде працювати, тому що ви можете оголосити батьківський клас десь в іншій збірці.
Простір імен Base.Assemble { громадський абстрактний клас батьків { внутрішня абстрактна void SomeMethod (); } // Це працює чудово, оскільки знаходиться в одній збірці. громадський клас ChildWithin: Батько { внутрішня переопрацювання пустоти SomeMethod () { } } } простір імен. Ще { // Kaboom, тому що ви не можете змінити внутрішній метод громадський клас ChildOutside: батьківський { } Тест громадського класу { //Просто добре приватний батько _parent; громадський тест () { // Ще добре _parent = новий ChildWithin (); } } }
Як бачите, це ефективно дозволяє комусь користуватися класом Parent, не маючи можливості успадкувати від.
Цей приклад містить два файли: Assembly1.cs і Assembly2.cs. Перший файл містить внутрішній базовий клас, BaseClass. У другому файлі спроба інстанціювати BaseClass призведе до помилки.
// 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
}
}
У цьому прикладі використовуйте ті самі файли, які ви використовували в прикладі 1, і змініть рівень доступності BaseClass на загальнодоступний . Змініть також рівень доступності IntM-члена на внутрішній . У цьому випадку ви можете створити екземпляр класу, але ви не можете отримати доступ до внутрішнього члена.
// 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
}
}
джерело : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx
Коли у вас є методи, класи тощо, які повинні бути доступні в межах поточної збірки і ніколи не знаходитися поза нею.
Наприклад, DAL може мати ORM, але об'єкти не повинні піддаватися діловому шару, вся взаємодія повинна здійснюватися статичними методами і передавати необхідні параметри.
Дуже цікаве використання внутрішнього - якщо внутрішній член, звичайно, обмежується лише тією збіркою, в якій він оголошений, - це отримати певну функцію "друга". Член-член - це те, що видно лише для деяких інших зборів поза межами асамблеї, в якій його заявлено. C # не має вбудованої підтримки для друга, однак CLR це робить.
Ви можете використовувати InternalsVisibleToAttribute для оголошення дружньої збірки, а всі посилання, що знаходяться в межах другої збірки, будуть розглядати внутрішні члени вашої декларувальної асамблеї як загальнодоступні в межах дружньої збірки. Проблема в цьому полягає в тому, що всі внутрішні члени видно; ви не можете вибрати.
Гарне використання для InternalsVisibleTo - піддавати різних внутрішніх членів тестовій збірці, таким чином, виключаючи потреби в складних роботах по рефлексії для тестування цих членів. Усі видимі внутрішні члени не є настільки великою проблемою, однак використання цього підходу досить сильно пригнічує інтерфейси вашого класу та може потенційно зруйнувати інкапсуляцію в межах декларувальної збірки.
Як правило, є два види членів:
Внутрішні класи дозволяють обмежити API вашої збірки. Це має переваги, як зробити ваш API простішим для розуміння.
Крім того, якщо помилка існує у вашій збірці, існує менший шанс виправлення, що вносить переломну зміну. Без внутрішніх класів ви повинні припустити, що зміна громадських членів будь-якого класу була б грубою зміною. За допомогою внутрішніх класів можна припустити, що зміна їхніх публічних членів лише порушує внутрішній API збірки (і будь-які збірки, на які посилається атрибут InternalsVisibleTo).
Мені подобається мати інкапсуляцію на рівні класу та на рівні складання. Є деякі, хто не погоджується з цим, але приємно знати, що функціонал доступний.
У мене є проект, який використовує LINQ-SQL для бек-енду даних. У мене є два основні простори імен: Biz та Data. Модель даних LINQ живе в даних і позначається як "внутрішня"; у просторі імен Biz є публічні класи, які обертаються навколо класів даних LINQ.
Так що є Data.Client
, і Biz.Client
; останні розкривають усі відповідні властивості об'єкта даних, наприклад:
private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }
Об'єкти Biz мають приватний конструктор (щоб змусити використовувати заводські методи) та внутрішній конструктор, який виглядає приблизно так:
internal Client(Data.Client client) {
this._client = client;
}
Це може бути використано будь-яким із бізнес-класів у бібліотеці, але інтерфейс (UI) не має можливості прямого доступу до моделі даних, гарантуючи, що бізнес-рівень завжди виступає посередником.
Це перший раз, коли я насправді internal
багато використовував , і це виявляється досить корисним.
Бувають випадки, коли є сенс складати членів класів internal
. Одним із прикладів може бути, якщо ви хочете контролювати, як класи інстанціюються; скажімо, ви надаєте якусь фабрику для створення екземплярів класу. Ви можете зробити конструктор internal
таким чином, що завод (який знаходиться в одній збірці) може створювати екземпляри класу, але код поза цією збіркою не може.
Однак я не бачу жодного сенсу робити заняття чи учасників internal
без конкретних причин, так само мало, як має сенс їх робити public
, або private
без конкретних причин.
єдине, що я коли-небудь використовував внутрішнє ключове слово, - це код перевірки ліцензії в моєму продукті ;-)
Одне використання внутрішнього ключового слова - обмеження доступу до конкретних реалізацій від користувача вашої збірки.
Якщо у вас є заводське або інше центральне місце для побудови об'єктів, користувачеві вашої збірки потрібно мати справу лише з відкритим інтерфейсом або абстрактним базовим класом.
Крім того, внутрішні конструктори дозволяють контролювати, де і коли інший публічний клас є інстанційним.
Як щодо цього: зазвичай рекомендується не виставляти об'єкт List зовнішнім користувачам збірки, а відкривати IEnumerable. Але набагато простіше використовувати об'єкт List всередині збірки, оскільки ви отримуєте синтаксис масиву та всі інші методи List. Отже, у мене зазвичай є внутрішня властивість, що розкриває список, який буде використовуватися всередині збірки.
Коментарі про такий підхід вітаються.
Майте на увазі, що будь-який клас, визначений як public
, автоматично відображатиметься у взаємозвученні, коли хтось перегляне вашу область імен проекту. З точки зору API, важливо лише показувати користувачам вашого проекту ті класи, якими вони можуть користуватися. Використовуйте internal
ключове слово, щоб приховати речі, які вони не повинні бачити.
Якщо ваш Big_Important_Class
проект для проекту A призначений для використання поза вашим проектом, не слід його позначати internal
.
Однак у багатьох проектах ви часто матимете заняття, призначені лише для використання у проекті. Наприклад, у вас може бути клас, який містить аргументи до параметризованого виклику потоку. У цих випадках ви повинні позначати їх так, internal
ніби з будь-якої іншої причини, ніж захистити себе від ненавмисної зміни API в дорозі.
private
Ключове слово може бути використано тільки на класи або структур, які визначені всередині іншого класу або структури. Клас, визначений у просторі імен, може бути оголошений лише public
або internal
.
Ідея полягає в тому, що коли ви проектуєте бібліотеку, загальнодоступними повинні бути лише класи, призначені для використання ззовні (клієнтами вашої бібліотеки). Таким чином ви можете приховати класи, які
тощо.
Якщо ви розробляєте внутрішні рішення, ніж використання внутрішніх елементів, це не так важливо, я думаю, адже зазвичай клієнти матимуть постійний контакт з вами та / або доступ до коду. Однак вони досить критичні для розробників бібліотек.
Коли у вас є класи або методи, які не вписуються чітко в об'єктно-орієнтовану парадигму, які роблять небезпечні речі, які потрібно викликати з інших підконтрольних вам класів і методів, і які ви не хочете, щоб хтось більше використовував .
public class DangerousClass {
public void SafeMethod() { }
internal void UpdateGlobalStateInSomeBizarreWay() { }
}