Альтернатива InternalsVisibleTo
В настоящее время я пытаюсь написать модульные тесты в своем решении, но я хочу поместить свои модульные тесты в другой отдельный проект. Проблема в том, что когда я генерирую некоторые поддельные данные тестирования, мне нужно установить свойства классов, но это невозможно, потому что свойства имеют частный/внутренний набор. Я нашел способ выставлять внутренние свойства только для некоторых проектов с использованием атрибута, и это прекрасно работает, но выглядит немного грязно и нечисто. Я ищу более дискретный способ выставить свои личные свойства другим проектам. Например, может быть, есть способ показать мои свойства без явного добавления атрибута внутри моих классов домена? Любая идея, как я могу это сделать?
РЕДАКТИРОВАТЬ: я нашел эту статью о настройке
InternalsVisibleTo
в вашем файле csproj. Кто-нибудь использовал это?https://bartwullems.blogspot.com/2020/06/internalsvisibleto-in-your-csproj-file.html
1 ответ
В этом сообщении блога описана альтернатива с примером кода на GitHub , в котором используется недокументированный атрибут для использования в сборке, которая обращается к закрытым/внутренним членам.
Однако обратите внимание на следующее:
- Это не задокументировано и не рекомендуется
- Вы не можете скомпилировать его напрямую, вместо этого вам нужно использовать Roslyn
- Также кажется, что он игнорирует доступ даже к закрытым членам, с чем, безусловно, следует быть очень осторожным.
Однако это может быть полезно, например, при работе с динамическими сборками и т. д., см. пример ниже.
Код
Вот что нужно сделать, в свой код добавитьIgnoresAccessChecksToAttribute
как в следующем коде (убедитесь, что он НЕ находится в каком-либо другом пространстве имен, иначе он не будет работать):
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
AssemblyName = assemblyName;
}
public string AssemblyName { get; }
}
}
а затем в любом месте вашего кода добавьте атрибут, как в:
[assembly: IgnoresAccessChecksTo("AssemblyToAccess")]
или в файле csproj:
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute">
<_Parameter1>AssemblyToAccess</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
Сборник
Компиляция сложна, так как ее нельзя скомпилировать стандартными средствами, вместо этого ее нужно вызывать через Roslyn, должно быть много доступных ресурсов о том, как компилировать динамически с помощью Roslyn, но вот конкретные детали, которые необходимо сделать для этой компиляции. работа:
var compilationOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication).
WithMetadataImportOptions(MetadataImportOptions.All);
var topLevelBinderFlagsProperty = typeof(CSharpCompilationOptions)
.GetProperty("TopLevelBinderFlags", BindingFlags.Instance | BindingFlags.NonPublic);
// 22 is the value of the undocumented member BinderFlags.IgnoreAccessibility
topLevelBinderFlagsProperty.SetValue(compilationOptions, (uint)1 << 22);
Использование в сборщике сборок
Одно законное использование - это динамическая компиляция сборок, которым требуется доступ к внутренним классам, см. мой ответ там для примера кода.