124

Having an assembly which I cannot modify (vendor-supplied) which have a method returning an object type but is really of an internal type.

How can I access the fields and/or methods of the object from my assembly?

Keep in mind that I cannot modify the vendor-supplied assembly.

In essence, here's what I have:

From vendor:

internal class InternalClass
  public string test;
end class

public class Vendor
  private InternalClass _internal;
  public object Tag {get{return _internal;}}
end class

From my assembly using the vendor assembly.

public class MyClass
{
  public void AccessTest()
  {
    Vendor vendor = new Vendor();
    object value = vendor.Tag;
    // Here I want to access InternalClass.test
  }
}
Liam
  • 27,717
  • 28
  • 128
  • 190
Stécy
  • 11,951
  • 16
  • 64
  • 89

6 Answers6

232

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.

starball
  • 20,030
  • 7
  • 43
  • 238
zonkflut
  • 2,929
  • 3
  • 22
  • 25
  • Expanding on the case of testing purposes. Any scenario where you have coupled internals in different contexts. For example, in Unity, you may have a runtime assembly, a test assembly, and an editor assembly. Runtime internals should be visible to test and editor assemblies. – Steve Buzonas Dec 27 '17 at 05:35
  • 13
    I don't think this answer helps - the OP says that he cannot modify the vendor's assembly, and this answer talks about modifying the AssemblyInfo.cs file of the vendor's project – Simon Green Oct 03 '18 at 07:47
  • To edit this info from inside Visual Studio, unload the project, right click on the project name, and click `Edit .csproj`. It'll be in XML format, but you'll be able to edit this same information. – Cullub Sep 22 '21 at 13:30
  • I followed your links, but still have no idea how to implement this. How do you actually reference from your .cs project the, let's say, .dll that contains all those internal classes? It mentions friends in the documentation, but there are no mentions of path or how to link them together. `"name of your assembly here"`, what should it actually contain if we're talking DLL vs CS project? – m3.b Jan 11 '22 at 04:54
88

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?

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Marc I wonder...it is possible to access private fields/properties but is there a way to cast the object returned by GetValue using the right type? – codingadventures Sep 15 '15 at 13:33
  • 1
    @GiovanniCampo if you know statically what the type is: sure - just cast. If you don't know the type statically - it is unclear what this would even *mean* – Marc Gravell Sep 15 '15 at 18:54
  • What about people who publish things as internals which are really part of the public API? Or they used `InternalsVisibleTo` but didn’t include your assembly? If the symbol isn’t truly hidden, it’s part of the ABI. – binki Oct 04 '17 at 23:05
11

In .NET 5 it's possible to add InternalsVisibleToAttribute to your .csproj:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
      <_Parameter1>Core.Tests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>
Damian Ubowski
  • 328
  • 4
  • 12
8

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"))
   // });
}
Ondrej Svejdar
  • 21,349
  • 5
  • 54
  • 89
  • 2
    For me, cannot be modified means that it comes from a nuget and I don’t want to have to create and manage a local nuget with the modifications. Also, for some people, the loss of a strong name will matter. But this is an interesting point. – binki Oct 04 '17 at 23:02
4

Reflection.

using System.Reflection;

Vendor vendor = new Vendor();
object tag = vendor.Tag;

Type tagt = tag.GetType();
FieldInfo field = tagt.GetField("test");

string value = field.GetValue(tag);

Use the power wisely. Don't forget error checking. :)

Colin Burnett
  • 11,150
  • 6
  • 31
  • 40
-3

Well, you can't. Internal classes can't be visible outside of their assembly, so no explicit way to access it directly -AFAIK of course. The only way is to use runtime late-binding via reflection, then you can invoke methods and properties from the internal class indirectly.

Ahmed
  • 11,063
  • 16
  • 55
  • 67