Практическое использование ключевого слова “internal” в C#

StackOverflow https://stackoverflow.com/questions/165719

  •  03-07-2019
  •  | 
  •  

Вопрос

Не могли бы вы, пожалуйста, объяснить, каково практическое применение для internal ключевое слово в C #?

Я знаю, что internal модификатор ограничивает доступ к текущей сборке, но когда и при каких обстоятельствах я должен его использовать?

Это было полезно?

Решение

Служебные или вспомогательные классы / методы, к которым вы хотели бы получить доступ из многих других классов в той же сборке, но которые вы хотите гарантировать, что код в других сборках не сможет получить к ним доступ.

От MSDN (через archive.org):

  

Широко используется внутренний доступ при разработке на основе компонентов, поскольку он позволяет группе компонентов взаимодействовать частным образом, не подвергаясь воздействию остального кода приложения. Например, платформа для построения графических пользовательских интерфейсов может предоставлять классы Control и Form, которые взаимодействуют, используя члены с внутренним доступом. Поскольку эти члены являются внутренними, они не подвергаются воздействию кода, который использует платформу.

Вы также можете использовать внутренний модификатор вместе с InternalsVisibleTo атрибут уровня сборки для создания " друга " сборки, которым предоставлен специальный доступ к внутренним классам целевой сборки.

Это может быть полезно для создания сборок модульного тестирования, которым затем разрешается вызывать внутренние элементы сборки, подлежащей проверке. Конечно, никаким другим сборкам не предоставляется такой уровень доступа, поэтому при выпуске системы инкапсуляция сохраняется.

Другие советы

Если Бобу нужен BigImportantClass, то Бобу нужно попросить людей, которые владеют проектом A, зарегистрироваться, чтобы гарантировать, что BigImportantClass будет написан для удовлетворения его потребностей, проверен на соответствие его потребностям, задокументирован как отвечающий его потребностям, и что будет внедрен процесс, чтобы гарантировать, что он никогда не будет изменен, чтобы больше не отвечать его потребностям.

Если класс является внутренним, ему не нужно проходить этот процесс, что экономит бюджет для проекта А, который они могут потратить на другие вещи.

Суть внутреннего не в том, что это усложняет жизнь Бобу. Это то, что он позволяет вам контролировать, какие дорогостоящие обещания Project A дает о функциях, сроке службы, совместимости и так далее.

Еще одна причина использовать внутреннюю - если вы запутываете свои двоичные файлы. Обфускатор знает, что зашифровать имя класса любых внутренних классов безопасно, в то время как имя открытых классов не может быть зашифровано, потому что это может сломать существующие ссылки.

Если вы пишете библиотеку DLL, которая содержит множество сложных функций в простом общедоступном API, то & # 8220; internal & # 8221; используется для членов класса, которые не должны быть публично раскрыты.

Сокрытие сложности (a.k.a. encapsulation) является главной концепцией качественной разработки программного обеспечения.

Внутреннее ключевое слово интенсивно используется при создании оболочки над неуправляемым кодом.

Если у вас есть библиотека на C / C ++, для которой вы хотите использовать DllImport, вы можете импортировать эти функции как статические функции класса и сделать их внутренними, чтобы у вашего пользователя был доступ только к вашей оболочке, а не к исходному API, поэтому не могу связываться ни с чем. Статические функции вы можете использовать везде в сборке для нужных вам классов-обёрток.

Вы можете взглянуть на Mono.Cairo, это оболочка Каирской библиотеки, которая использует этот подход.

При использовании " используйте как можно более строгий модификатор " Правило Я использую Internal везде, где мне нужен доступ, скажем, к методу из другого класса, пока мне явно не понадобится доступ к нему из другой сборки.

Поскольку интерфейс сборки обычно более узок, чем сумма интерфейсов классов, я использую его довольно много.

Я считаю, что внутреннее слишком сильно используется. Вы действительно не должны подвергать определенную функциональную неспособность только определенным классам, что не сделали бы другие потребители.

Это, на мой взгляд, нарушает интерфейс, нарушает абстракцию. Это не значит, что его никогда не следует использовать, но лучшим решением является рефакторинг в другой класс или использование по-другому, если это возможно. Однако это не всегда возможно.

Причины, по которым это может вызвать проблемы, заключаются в том, что другому разработчику может быть поручено создать другой класс в той же сборке, что и у вас. Наличие внутренних элементов уменьшает ясность абстракции и может вызвать проблемы при неправильном использовании. Это было бы той же проблемой, как если бы вы сделали это публично. Другой класс, который создается другим разработчиком, по-прежнему является потребителем, как и любой внешний класс. Абстракция и инкапсуляция классов предназначены не только для защиты внешних классов, но и для любых классов.

Еще одна проблема заключается в том, что многие разработчики подумают , что им может понадобиться использовать его где-нибудь в сборке и пометить его как внутренний в любом случае, даже если он им и не нужен в данный момент. Тогда другой разработчик может подумать, что его можно взять. Как правило, вы хотите пометить частное, пока у вас есть определительная потребность.

Но кое-что из этого может быть субъективным, и я не говорю, что его никогда не следует использовать. Просто используйте при необходимости.

На днях, может быть, неделю увидел интересный блог, который я не могу вспомнить. По сути, я не могу взять кредит на себя, но я подумал, что у него может быть какое-то полезное приложение.

Скажем, вы хотели, чтобы абстрактный класс был виден другой сборке, но вы не хотите, чтобы кто-то мог унаследовать его. Sealed не будет работать, потому что по какой-то причине он абстрактный, другие классы в этой сборке наследуют его. Закрытое не будет работать, потому что вы можете объявить родительский класс где-то в другой сборке.

namespace Base.Assembly
{
  public abstract class Parent
  {
    internal abstract void SomeMethod();
  }

  //This works just fine since it's in the same assembly.
  public class ChildWithin : Parent
  {
    internal override void SomeMethod()
    {
    }
  }
}

namespace Another.Assembly
{
  //Kaboom, because you can't override an internal method
  public class ChildOutside : Parent
  {
  }

  public class Test 
  { 

    //Just fine
    private Parent _parent;

    public Test()
    {
      //Still fine
      _parent = new ChildWithin();
    }
  }
}

Как вы можете видеть, он эффективно позволяет кому-либо использовать родительский класс без возможности наследования от него.

Этот пример содержит два файла: 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 на public . Также измените уровень доступности члена 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
   }
}

source : http: //msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx

Очень интересное использование внутреннего - конечно, внутренний элемент ограничен только сборкой, в которой он объявлен - получает "друга" функциональность в некоторой степени из этого. Друг друг - это то, что видно только определенным другим сборкам за пределами сборки, в которой он объявлен. C # не имеет встроенной поддержки для друга, однако CLR делает.

Вы можете использовать InternalsVisibleToAttribute , чтобы объявите дружественную сборку, и все ссылки из дружественной сборки будут относиться к внутренним элементам вашей объявленной сборки как общедоступным в рамках дружественной сборки. Проблема в том, что все внутренние элементы видны; вы не можете выбирать.

Хорошее использование InternalsVisibleTo - выставлять различные внутренние элементы в сборку модульного теста, что устраняет необходимость в сложных обходных решениях для тестирования этих элементов. Все внутренние видимые элементы не являются большой проблемой, однако использование этого подхода довольно сильно портит интерфейсы вашего класса и потенциально может разрушить инкапсуляцию в объявленной сборке.

Если у вас есть методы, классы и т. д., которые должны быть доступны в рамках текущей сборки, а не за ее пределами.

Например, DAL может иметь ORM, но объекты не должны подвергаться бизнес-уровню, все взаимодействие должно осуществляться статическими методами и передачей необходимых параметров.

Как правило, существует два типа членов:

  • общественная поверхность:видимый из внешней сборки (общедоступной, защищенной и защищенной внутри):вызывающий объект не является доверенным, поэтому проверка параметров, документация по методу и т.д.необходим.
  • частная поверхность:не виден из внешней сборки (private и internal или внутренние классы):вызывающему объекту, как правило, доверяют, поэтому проверка параметров, документация по методу и т.д.может быть опущен.

Снижение шума: чем меньше типов вы выставляете, тем проще ваша библиотека. Защита от несанкционированного доступа / защита - это еще одна вещь (хотя Reflection может победить ее).

Внутренние классы позволяют вам ограничивать API вашей сборки. Это имеет свои преимущества, например упрощает понимание вашего API.

Кроме того, если в вашей сборке существует ошибка, вероятность исправления, вносящего критическое изменение, меньше. Без внутренних классов вы должны были бы предположить, что изменение открытых членов любого класса было бы серьезным изменением. Что касается внутренних классов, то можно предположить, что изменение их открытых элементов нарушает только внутренний API сборки (и любые сборки, на которые есть ссылка в атрибуте InternalsVisibleTo).

Мне нравится иметь инкапсуляцию на уровне класса и на уровне сборки. Есть некоторые, кто не согласен с этим, но приятно знать, что функциональность доступна.

У меня есть проект, который использует LINQ-to-SQL для обработки данных. У меня есть два основных пространства имен: Biz и Data. Модель данных LINQ находится в Data и помечена как «внутренняя»; пространство имен 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 , и это оказывается весьма полезным.

Бывают случаи, когда имеет смысл сделать членов классов внутренними . Одним из примеров может быть, если вы хотите контролировать, как создаются экземпляры классов; допустим, вы предоставляете какую-то фабрику для создания экземпляров класса. Вы можете сделать конструктор внутренним , чтобы фабрика (которая находится в той же сборке) могла создавать экземпляры класса, а код вне этой сборки не может.

Однако я не вижу смысла в том, чтобы делать классы или члены внутренними без особых причин, настолько же мало, как это имеет смысл делать их общедоступными или < code> private без особых причин.

единственное, что я когда-либо использовал ключевое слово internal, - это код проверки лицензии в моем продукте; -)

Одно из применений внутреннего ключевого слова - ограничение доступа к конкретным реализациям со стороны пользователя вашей сборки.

Если у вас есть фабрика или другое центральное место для конструирования объектов, пользователю вашей сборки нужно иметь дело только с открытым интерфейсом или абстрактным базовым классом.

Кроме того, внутренние конструкторы позволяют вам контролировать, где и когда создается экземпляр общедоступного класса.

Как насчет этого: обычно рекомендуется не предоставлять объект List внешним пользователям сборки, а предоставлять IEnumerable. Но намного проще использовать объект List внутри сборки, потому что вы получаете синтаксис массива и все другие методы List. Итак, у меня обычно есть внутреннее свойство, предоставляющее список для использования внутри сборки.

Комментарии приветствуются.

Имейте в виду, что любой класс, определенный как public , будет автоматически отображаться в intellisense, когда кто-то смотрит на пространство имен вашего проекта. С точки зрения API важно показывать пользователям вашего проекта только те классы, которые они могут использовать. Используйте ключевое слово internal , чтобы скрыть вещи, которые они не должны видеть.

Если ваш Big_Important_Class для проекта A предназначен для использования вне вашего проекта, вам не следует отмечать его internal .

Однако во многих проектах у вас часто есть классы, которые действительно предназначены только для использования внутри проекта. Например, у вас может быть класс, который содержит аргументы для вызова параметризованного потока. В этих случаях вы должны пометить их как internal , если не по какой-либо другой причине, кроме как защитить себя от непреднамеренного изменения API в будущем.

Идея заключается в том, что при разработке библиотеки общедоступными должны быть только те классы, которые предназначены для использования извне (клиентами вашей библиотеки).Таким образом, вы можете скрыть классы, которые

  1. Скорее всего, они изменятся в будущих выпусках (если бы они были общедоступными, вы бы взломали клиентский код).
  2. Бесполезны для клиента и могут вызвать путаницу
  3. Небезопасны (поэтому неправильное использование может привести к серьезному повреждению вашей библиотеки).

и т.д.

Если вы разрабатываете собственные решения, то, я думаю, использование внутренних элементов не так важно, потому что обычно клиенты будут иметь постоянный контакт с вами и / или доступ к коду.Однако они довольно важны для разработчиков библиотек.

Если у вас есть классы или методы, которые не совсем вписываются в объектно-ориентированную парадигму, которые делают опасные вещи, которые нужно вызывать из других классов и методов, находящихся под вашим контролем, и которые вы не хотите допускать кто-нибудь еще использует.

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top