Comment puis-je accéder à une classe interne à partir d'un assembly externe?

Ayant un assembly que je ne peux pas modifier (fourni par le fournisseur) qui a une méthode renvoyant un objet type mais est vraiment d'un type interne.

Comment puis-je accéder aux champs et / ou méthodes de l'objet depuis mon assembly?

Gardez à l'esprit que je ne peux pas modifier l'assemblage fourni par le fournisseur.

, En substance, voici ce que j'ai:

Du vendeur:

internal class InternalClass
  public string test;
end class

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

De mon assembly en utilisant l'assembly vendor.

public class MyClass
{
  public void AccessTest()
  {
    Vendor vendor = new Vendor();
    object value = vendor.Tag;
    // Here I want to access InternalClass.test
  }
}
77
demandé sur Liam 2009-05-28 17:27:57

5 réponses

Sans accès au type (et pas de "InternalsVisibleTo" etc.), vous devrez utiliser la réflexion. Mais une meilleure question serait: devriez-vous accéder à ces données? Il ne fait pas partie du contrat de type public... il me semble qu'il est destiné à être traité comme un objet opaque (pour leurs fins, pas le vôtre).

Vous l'avez décrit comme un champ d'instance publique; pour l'obtenir par réflexion:

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

S'il s'agit en fait d'une propriété (pas d'un champ):

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

Si il n'est pas public, vous devrez utiliser la surcharge BindingFlags de GetField/GetProperty.

Important à part: soyez prudent avec une réflexion comme celle-ci; l'implémentation pourrait changer dans la prochaine version (casser votre code), ou elle pourrait être obscurcie (casser votre code), ou vous pourriez ne pas avoir assez de "confiance" (casser votre code). Êtes-vous repérer le motif?

75
répondu Marc Gravell 2009-05-28 13:38:52

Je ne vois qu'un seul cas où vous autoriseriez l'exposition de vos membres internes à une autre assemblée et c'est à des fins de test.

Dire qu'il existe un moyen d'autoriser les assemblys "amis" à accéder aux internes:

Dans AssemblyInfo.fichier cs du projet vous ajoutez une ligne pour chaque assemblage.

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

Cette information est disponible ici.

J'espère que cela aide.

185
répondu zonkflut 2009-06-12 00:52:31

Réflexion.

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

Utilisez le pouvoir à bon escient. Ne pas oublier la vérification des erreurs. :)

3
répondu Colin Burnett 2009-05-28 13:34:46

Je voudrais argumenter sur un point - que vous ne pouvez pas augmenter l'assemblage d'origine - en utilisant Mono.Cecil vous pouvez injecter [InternalsVisibleTo(...)] à l'assemblage 3pty. Notez qu'il peut y avoir des implications juridiques-vous jouez avec l'assemblage 3pty et des implications techniques-si l'assemblage a un nom fort, vous devez le dépouiller ou le re-signer avec une clé différente.

 Install-Package Mono.Cecil

Et le code comme:

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"))
   // });
}
3
répondu Ondrej Svejdar 2017-06-06 10:02:03

Eh bien, vous ne pouvez pas. les classes internes ne peuvent pas être visibles en dehors de leur assemblage, donc pas de moyen explicite d'y accéder directement-AFAIK bien sûr. Le seul moyen est d'utiliser la liaison tardive d'exécution via la réflexion, alors vous pouvez appeler indirectement les méthodes et les propriétés de la classe interne.

-3
répondu Galilyou 2009-05-28 13:34:42