Атрибут InternalsVisibleTo не работает

Я пытаюсь использовать InternalsVisibleTo Атрибут ассемблера, чтобы сделать мои внутренние классы в библиотеке классов.NET видимыми для моего проекта модульного теста. По какой-то причине я продолжаю получать сообщение об ошибке:

MyClassName недоступен из-за уровня защиты

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

24 ответа

Решение

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

[assembly: InternalsVisibleTo("MyFriendAssembly,
PublicKey=0024000004800000940000000602000000240000525341310004000001000100F73
F4DDC11F0CA6209BC63EFCBBAC3DACB04B612E04FA07F01D919FB5A1579D20283DC12901C8B66
A08FB8A9CB6A5E81989007B3AA43CD7442BED6D21F4D33FB590A46420FB75265C889D536A9519
674440C3C2FB06C5924360243CACD4B641BE574C31A434CE845323395842FAAF106B234C2C140
6E2F553073FF557D2DB6C5")]

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

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

В моем случае у меня был проект Thingamajig и сопутствующий проект ThingamajigAutoTests (имена изменены, чтобы защитить виновных), которые оба произвели неподписанные сборки. Я должным образом добавил атрибут [assembly: InternalsVisibleTo( "ThingamajigAutoTests" )] в файл Thingamajig\AssemblyInfo.cs и закомментировал AssemblyKeyFile а также AssemblyKeyName атрибуты, как отмечено выше. Thingamajig Проект построен только штраф, но его внутренние члены упорно не обнаруживаться в проекте автотестов.

После сильной царапины я перепроверил ThingamajigAutoTests свойства проекта и обнаружил, что имя сборки было указано как "ThingamajigAutoTests.dll". Бинго - я добавил расширение.dll к имени сборки в InternalsVisibleTo атрибут, и кусочки встали на свои места.

Иногда это мелочи...

Если ваши сборки не подписаны, но вы все равно получаете ту же ошибку, проверьте файл AssemblyInfo.cs на наличие одной из следующих строк:

[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

Вкладка свойств будет по-прежнему отображать вашу сборку как беззнаковую, если присутствует одна (или обе) из этих линий, но атрибут InternalsVisibleTo рассматривает сборку с этими линиями как строго подписанную. Просто удалите (или закомментируйте) эти строки, и это должно работать для вас.

Стоит отметить, что если сборка "друг" (тесты) написана на C++/CLI, а не на C#/VB.Net, то вам необходимо использовать следующее:

#using "AssemblyUnderTest.dll" as_friend

вместо ссылки на проект или обычный #using заявление. По какой-то причине это невозможно сделать в справочном интерфейсе проекта.

Хотя с помощью AssemblyInfoфайл и добавление InternalsVisibleToвсе еще работает, теперь предпочтительным подходом (насколько я могу судить) является использование ItemGroupв вашем проекте csprojфайл примерно так:

          <ItemGroup>
        <InternalsVisibleTo Include="My.Project.Tests" />
    </ItemGroup>

Если требуется PublicKey, этот атрибут также может быть добавлен.

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

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

Вот макрос, который я использую для быстрой генерации этого атрибута. Это немного странно, но это работает. На моей машине. Когда последний подписанный двоичный файл находится в /bin/debug, И т.д. Двусмысленность и т.д. В любом случае, вы можете увидеть, как он получает ключ, так что это даст вам подсказку. Исправить / улучшить, как позволяет ваше время.

Sub GetInternalsVisibleToForCurrentProject()
    Dim temp = "[assembly:  global::System.Runtime.CompilerServices." + _
               "InternalsVisibleTo(""{0}, publickey={1}"")]"
    Dim projs As System.Array
    Dim proj As Project
    projs = DTE.ActiveSolutionProjects()
    If projs.Length < 1 Then
        Return
    End If

    proj = CType(projs.GetValue(0), EnvDTE.Project)
    Dim path, dir, filename As String
    path = proj.FullName
    dir = System.IO.Path.GetDirectoryName(path)
    filename = System.IO.Path.GetFileNameWithoutExtension(path)
    filename = System.IO.Path.ChangeExtension(filename, "dll")
    dir += "\bin\debug\"
    filename = System.IO.Path.Combine(dir, filename)
    If Not System.IO.File.Exists(filename) Then
        MsgBox("Cannot load file " + filename)
        Return
    End If
    Dim assy As System.Reflection.Assembly
    assy = System.Reflection.Assembly.Load(filename)
    Dim pk As Byte() = assy.GetName().GetPublicKey()
    Dim hex As String = BitConverter.ToString(pk).Replace("-", "")
    System.Windows.Forms.Clipboard.SetText(String.Format(temp, assy.GetName().Name, hex))
    MsgBox("InternalsVisibleTo attribute copied to the clipboard.")
End Sub

Вы должны использовать переключатель /out: compiler при компиляции сборки-друга (сборки, которая не содержит атрибут InternalsVisibleTo).

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

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

[assembly: InternalsVisibleTo("assemblyname,
PublicKey="Full Public Key")]

Выполните следующие шаги MSDN, чтобы сгенерировать новый полный открытый ключ для сборки из Visual Studio.

Чтобы добавить элемент "Получить открытый ключ сборки" в меню "Инструменты"

В Visual Studio выберите Внешние инструменты в меню Сервис.

В диалоговом окне "Внешние инструменты" нажмите " Добавить" и введите "Получить открытый ключ сборки" в поле "Название".

Заполните поле Command, перейдя к sn.exe. Обычно он устанавливается в следующем месте: C: \ Program Files (x86) \ Microsoft SDKs \ Windows \ v7.0a \ Bin \ x64 \ sn.exe.

В поле "Аргументы" введите следующее (с учетом регистра): -Tp $ (TargetPath). Установите флажок "Использовать вывод".

Нажмите ОК. Новая команда добавлена ​​в меню Инструменты.

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

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

1. Подпишите тестовый проект. В Visual Studio перейдите в окно свойств тестового проекта и подпишите сборку, установив флажок с той же фразой на вкладке Подписывание.

2. Создайте PublicKey для тестового проекта: откройте командную строку Visual Studio (например, командную строку разработчика для VS 2017). Перейдите в папку, где находится файл.dll тестового проекта. Создайте открытый ключ через sn.exe:

sn -Tp TestProject.dll

Обратите внимание, что аргумент -Tp, но не -tp.

3. Представьте PublicKey в тестируемом проекте: перейдите к файлу AssemblyInfo.cs в тестируемом проекте и добавьте эту строку с PublicKey, созданным на предыдущем шаге:

[сборка: InternalsVisibleTo("TestProjectAssemblyName,ОткрытыйКлюч=2066212d128683a85f31645c60719617ba512c0bfdba6791612ed56350368f6cc40a17b4942ff16cda9e760684658fa3f357c137a1005b04cb002400000480000094000000060200000024000052534131000400000100010065fe67a14eb30ffcdd99880e9d725f04e5c720dffc561b23e2953c34db8b7c5d4643f476408ad1b1e28d6bde7d64279b0f51bf0e60be2d383a6c497bf27307447506b746bd2075")]

Не забудьте заменить вышеуказанный PublicKey на ваш.

4. Сделайте приватный метод внутренним. В тестируемом проекте измените модификатор доступа метода на внутренний.

внутренняя статическая пустота DoSomething(){...}

В моем случае с использованием VS.Net 2015 мне нужно было подписать ОБА сборки (если хотя бы одна сборка должна быть подписана или вы хотите сослаться на открытый ключ вашей сборки).

Мой проект вообще не использовал подпись. Поэтому я начал добавлять ключ подписи в свою тестовую библиотеку и использовать InternalsVisibleTo-Attribute в базовой библиотеке моего проекта. Но VS.Net всегда объяснял, что не может получить доступ к методам друзей.

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

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

  1. Вы вызываете внутренний метод, определенный в X, из другой сборки Y
  2. Подпись метода использует внутренние типы, определенные в Z
  3. Затем вы должны добавить [InternalsVisibleTo] в X и в Z

Например:

// In X
internal static class XType
{
    internal static ZType GetZ() { ... }
}

// In Y:
object someUntypedValue = XType.GetZ();

// In Z:
internal class ZType { ... }

Если вы написали это как выше, где вы не ссылаетесь на ZType непосредственно в Y, после добавления Y в качестве друга X, вы можете быть озадачены, почему ваш код все еще не компилируется.

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

Я пишу это из-за разочарования. Убедитесь, что сборка, доступ к которой вы предоставляете, названа так, как вы ожидаете.

Я переименовал свой проект, но это не приводит к автоматическому обновлению имени сборки. Щелкните правой кнопкой мыши ваш проект и выберите Свойства. В разделе " Приложение" убедитесь, что имя сборки и пространство имен по умолчанию соответствуют вашим ожиданиям.

Предыдущие ответы с PublicKey сработали: (Visual Studio 2015: ДОЛЖЕН быть в одной строке, в противном случае он жалуется, что ссылка на сборку недействительна или на нее нельзя ссылаться. PublicKeyToken не сработал)

[assembly: InternalsVisibleTo("NameSpace.MyFriendAssembly, PublicKey=0024000004800000940000000602000000240000525341310004000001000100F73F4DDC11F0CA6209BC63EFCBBAC3DACB04B612E04FA07F01D919FB5A1579D20283DC12901C8B66A08FB8A9CB6A5E81989007B3AA43CD7442BED6D21F4D33FB590A46420FB75265C889D536A9519674440C3C2FB06C5924360243CACD4B641BE574C31A434CE845323395842FAAF106B234C2C1406E2F553073FF557D2DB6C5")]

Благодаря @Joe

Чтобы получить открытый ключ сборки друзей:

sn -Tp path\to\assembly\MyFriendAssembly.dll

Внутри командной строки Developper (Автозагрузка> Программы> Visual Studio 2015 > Инструменты Visual Studio> Командная строка разработчика для VS2015). Благодаря @Ian G.

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

У меня такая же проблема. Ни одно из решений не сработало.

В конечном итоге обнаруженная проблема была связана с тем, что класс X явно реализовал интерфейс Y, который является внутренним.

метод X.InterfaceMethod был недоступен, хотя я понятия не имею, почему.

Решением было привести (X как YourInterface).InterfaceMethod в тестовую библиотеку, и тогда все заработало.

Применяется только в том случае, если вы хотите сохранить неподписанные сборки как неподписанные сборки (и не хотите подписывать их по нескольким причинам):

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

НО: как только вы компилируете базовую библиотеку на сетевой диск, применяются политики безопасности, и сборка не может быть успешно загружена. Это снова приводит к сбою VS.NET или компилятора при проверке соответствия PublicKey.

Наконец, можно использовать неподписанные сборки: https://msdn.microsoft.com/en-us/library/bb384966.aspx Вы должны убедиться, что ОБА сборки не ПОДПИСАНЫ, а атрибут Assembly должен быть без информации PublicKey:

<Assembly: InternalsVisibleTo("friend_unsigned_B")>

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

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

private ISomeService _serviceне сработало, ноprivate SomeService _serviceделал. И это имеет смысл, поскольку внутренние методы, которые у меня были, не были частью интерфейса.

Что бы это ни стоило, я обнаружил, что свойство, которое было недоступно из-за его уровня защиты:

      MyField field;

пришлось изменить на:

      internal MyField field;

А потом скомпилировал. я думал такinternalбыл модификатор доступа по умолчанию?

Если в файле проекта указано InternalsVisibleTo, убедитесь, что вы также не указали:

<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

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

      <Choose>
    <When Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
      <PropertyGroup>
          <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
      </PropertyGroup>
      <ItemGroup>
          <InternalsVisibleTo Include="my.project.Test" />
      </ItemGroup>
    </When>
    <When Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
      <PropertyGroup>
          <AssemblyOriginatorKeyFile>my.project.snk</AssemblyOriginatorKeyFile>
      </PropertyGroup>
    </When>
</Choose> 

А затем скомпилируйте тестовые классы только в версии Debug, используя:

      #if DEBUG
    ...
#endif

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

Смена регистра ключа с верхнего на нижний регистр устранила проблему.

InternalsVisibleToчасто используется в контексте тестирования. Если вы используете фиктивный фреймворк, такой как , вам нужно будет открыть внутренности вашего проекта для Moqтакже. В конкретном случае этой структуры мне также нужно было добавить
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]к моему AssemblyInfo.

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

Другие вопросы по тегам