Альтернатива 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);

Использование в сборщике сборок

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

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