Alternative to InternalsVisibleTo
Asked Answered
C

1

2

I am currently trying to write unit tests in my solution, but I want to put my unit tests in a different separate project. The problem is when I am generating some fake testing data I need to set properties of classes, but this is impossible because properties have private/internal set. I found a way to expose internal properties only to some projects with using the attribute InternalsVisibleTo, and this works great, but it looks a little bit messy and unclean. I am looking for more discrete way to expose my private properties to other projects. For example maybe there is a way to expose my properties without explicitly adding an attribute inside of my domain classes? Any idea how I can do this?

EDIT: I found this article about setting InternalsVisibleTo in your csproj file. Has anyone used this? https://bartwullems.blogspot.com/2020/06/internalsvisibleto-in-your-csproj-file.html

Catch answered 2/2, 2021 at 9:40 Comment(4)
The link you've edited in - that's still using InternalsVisibleTo. If you're considering using it, then it's even more unclear than it was before what, exactly, you consider "unclean" or "messy" about it.Birdsong
Yes, I am still using it but inside in the csproj file so it's more discrete. The problem before was that in all of my domain classes I had to explicitly add InternalsVisibleTo, so If I have 100 files with classes in my Domain project, I had to add 100 InternalsVisibleTo Attributes. With this approach I will only need 3 new lines of code in my csproj file. I haven't tried it yet, so I am not sure if it will work, but I think this will be the perfect solution if it does work.Catch
No, you didn't have to add it to multiple classes. All of the examples in the documentation show it being used as an assembly attribute, which of course only has to be specified once in the project. In fact, it's not even valid to try to apply it at the class level.Birdsong
I guess I didn't get that idea from the documentation, because in the examples shown the attribute is actually added inside of a class. Thank you for your comments.Catch
A
2

There is an alternative described in this blog post with sample code on GitHub, which uses an undocumented attribute to be used in the assembly that accesses the private/internal members.

Note however the following:

  • This is not documented and not recommended
  • You cannot compile it directly, instead you need to use Roslyn
  • It also appears to ignore access even to private members which is definitely something to be very careful with

It might however be useful for example when dealing with dynamic assemblies etc, see example below.

The Code

Here is what needs to be done, in your code add the IgnoresAccessChecksToAttribute as in the following code (make sure it is NOT under any other namespace or it will not work):

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
    public class IgnoresAccessChecksToAttribute : Attribute
    {
        public IgnoresAccessChecksToAttribute(string assemblyName)
        {
            AssemblyName = assemblyName;
        }

        public string AssemblyName { get; }
    }
}

and then anywhere in your code add the attribute as in:

[assembly: IgnoresAccessChecksTo("AssemblyToAccess")]

or in the csproj file:

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute">
     <_Parameter1>AssemblyToAccess</_Parameter1>
   </AssemblyAttribute>
</ItemGroup>

Compilation

Compilation is tricky as it cannot be compiled with standard means, instead it has to be called via Roslyn, there should be many resources available on how to compile dynamically with Roslyn, but here is the particular details that needs to be done for this compilation to work:

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);

Usage in Assembly Builder

One legitimate use is when compiling dynamically assemblies that needs access to internal classes, see my answer there for sample code.

Argyle answered 17/6, 2022 at 0:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.