Avoir un assemblage que je ne peux pas modifier (fourni par le fournisseur) et dont la méthode renvoie un objet mais qui est en réalité un type interne.
Comment puis-je accéder aux champs et/ou aux méthodes de l'objet à partir de mon assemblage?
N'oubliez pas que je ne peux pas modifier l'assembly 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 assemblée en utilisant l'assembleur du vendeur.
public class MyClass
{
public void AccessTest()
{
Vendor vendor = new Vendor();
object value = vendor.Tag;
// Here I want to access InternalClass.test
}
}
Sans accès au type (et sans "InternalsVisibleTo", etc.), vous devrez utiliser la réflexion. Mais une meilleure question serait: devriez-vous accéder à ces données? Cela ne fait pas partie du contrat de type public ... il me semble qu'il est destiné à être traité comme un objet opaque (à leurs fins, pas à vous).
Vous l'avez décrit comme un champ d'instance publique. pour obtenir ceci 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);
S'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 être obscurcie (casser votre code) ou vous pourriez ne pas avoir assez de "confiance" (casser votre code). Êtes-vous en train de repérer le motif?
Je ne vois qu'un cas dans lequel vous autoriseriez une exposition de vos membres internes à une autre assemblée, à des fins de test.
Dire qu'il existe un moyen d'autoriser les assemblys "amis" à accéder aux internes:
Dans le fichier AssemblyInfo.cs du projet, vous ajoutez une ligne pour chaque assembly.
[Assembly: InternalsVisibleTo("name of Assembly here")]
cette information est disponible ici.
J'espère que cela t'aides.
Je voudrais faire valoir un point - vous ne pouvez pas augmenter l’Assemblée originale - en utilisant Mono.Cecil, vous pouvez injecter [InternalsVisibleTo(...)]
à l’Assemblée 3pty. Notez qu'il peut y avoir des implications juridiques - vous vous occupez de l'Assemblée vide et des implications techniques - si l'Assemblée a un nom fort, vous devez la supprimer ou la signer à nouveau 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"))
// });
}
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. N'oubliez pas de vérifier les erreurs. :)