Практическое использование для «внутреннего» ключевого слова в C # - PullRequest
383 голосов
/ 03 октября 2008

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

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

Ответы [ 22 ]

345 голосов
/ 03 октября 2008

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

С MSDN (через archive.org):

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

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

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

81 голосов
/ 10 июня 2010

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

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

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

57 голосов
/ 04 октября 2008

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

27 голосов
/ 03 октября 2008

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

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

20 голосов
/ 04 октября 2008

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

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

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

12 голосов
/ 03 октября 2008

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

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

10 голосов
/ 03 октября 2008

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

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

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

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

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

8 голосов
/ 03 октября 2008

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

Скажем, вы хотели, чтобы абстрактный класс был виден другой сборке, но вы не хотите, чтобы кто-то мог унаследовать его. Sealed не будет работать, потому что по какой-то причине он абстрактный, другие классы в этой сборке наследуют его. 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();
    }
  }
}

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

7 голосов
/ 11 июня 2010

Этот пример содержит два файла: 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
   }
}

источник : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx

6 голосов
/ 03 октября 2008

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

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

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

...