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


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

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

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

18 66

18 ответов:

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

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

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

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

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

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

иногда мелочь...

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

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

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

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

#using "AssemblyUnderTest.dll" as_friend

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

Колин

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

вот макрос, который я использую для быстрого создания этого атрибута. Его немного hacky, но это работает. На моей машине. Когда последний подписанный двоичный файл находится в /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).

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

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

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

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

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

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

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

спасибо @Joe

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

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

внутри командной строки разработчика (запуск > программы > Visual Studio 2015 > инструменты Visual Studio > Командная строка разработчика для VS2015). Спасибо @Ian Г.

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

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

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

следуйте ниже MSDN шаги по созданию нового полного открытого ключа для сборки из visual studio.

чтобы добавить пункт Get Assembly Public Key в меню Сервис

в Visual Studio щелкните Внешние Инструменты в меню Инструменты.

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

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

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

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

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

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

  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, вы можете быть озадачены, почему ваш код все еще не компилируется.

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

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

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

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

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

<Assembly: InternalsVisibleTo("friend_unsigned_B")>

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

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

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

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

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

решение состояло в том, чтобы бросить (X как ваш интерфейс).InterfaceMethod в тестовой библиотеке, а затем все работало.

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

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

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

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