How can I access an internal class from an external assembly?

Solution 1:

I see only one case that you would allow exposure to your internal members to another assembly and that is for testing purposes.

Saying that there is a way to allow "Friend" assemblies access to internals:

In the AssemblyInfo.cs file of the project you add a line for each assembly.

[assembly: InternalsVisibleTo("name of assembly here")]

this info is available here.

Hope this helps.

Solution 2:

Without access to the type (and no "InternalsVisibleTo" etc) you would have to use reflection. But a better question would be: should you be accessing this data? It isn't part of the public type contract... it sounds to me like it is intended to be treated as an opaque object (for their purposes, not yours).

You've described it as a public instance field; to get this via reflection:

object obj = ...
string value = (string)obj.GetType().GetField("test").GetValue(obj);

If it is actually a property (not a field):

string value = (string)obj.GetType().GetProperty("test").GetValue(obj,null);

If it is non-public, you'll need to use the BindingFlags overload of GetField/GetProperty.

Important aside: be careful with reflection like this; the implementation could change in the next version (breaking your code), or it could be obfuscated (breaking your code), or you might not have enough "trust" (breaking your code). Are you spotting the pattern?

Solution 3:

I would like to argue one point - that you cannot augment the original assembly - using Mono.Cecil you can inject [InternalsVisibleTo(...)] to the 3pty assembly. Note there might be legal implications - you're messing with 3pty assembly and technical implications - if the assembly has strong name you either need to strip it or re-sign it with different key.

 Install-Package Mono.Cecil

And the code like:

static readonly string[] s_toInject = {
  // alternatively "MyAssembly, PublicKey=0024000004800000... etc."
  "MyAssembly"
};

static void Main(string[] args) {
  const string THIRD_PARTY_ASSEMBLY_PATH = @"c:\folder\ThirdPartyAssembly.dll";

   var parameters = new ReaderParameters();
   var asm = ModuleDefinition.ReadModule(INPUT_PATH, parameters);
   foreach (var toInject in s_toInject) {
     var ca = new CustomAttribute(
       asm.Import(typeof(InternalsVisibleToAttribute).GetConstructor(new[] {
                      typeof(string)})));
     ca.ConstructorArguments.Add(new CustomAttributeArgument(asm.TypeSystem.String, toInject));
     asm.Assembly.CustomAttributes.Add(ca);
   }
   asm.Write(@"c:\folder-modified\ThirdPartyAssembly.dll");
   // note if the assembly is strongly-signed you need to resign it like
   // asm.Write(@"c:\folder-modified\ThirdPartyAssembly.dll", new WriterParameters {
   //   StrongNameKeyPair = new StrongNameKeyPair(File.ReadAllBytes(@"c:\MyKey.snk"))
   // });
}