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

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

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

22 ответов


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

С MSDN:

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

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

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


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

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

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


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


Если вы пишете DLL, которая инкапсулирует тонну сложных функций в простой открытый API, то "внутренний" используется для членов класса, которые не должны быть открыты публично.

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


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

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

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


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

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


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

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

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

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

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


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

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

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.цезий. Первый файл содержит внутренний базовый класс 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 на внутренние. В этом случае можно создать экземпляр class, но вы не можете получить доступ к внутреннему члену.

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


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

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

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


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

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


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


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

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

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

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

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


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


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


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

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

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


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

комментарии приветствуются по поводу этого подхода.


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

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

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


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

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

etc.

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


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

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}