2009-05-28 2 views
72

Avere un gruppo che non posso modificare (fornito dal produttore), che dispone di un metodo che restituisce un oggetto tipo, ma è in realtà di un tipo interno.C# - Come accedere classe interna da montaggio esterno

Come posso accedere ai campi e/o ai metodi dell'oggetto dal mio assieme?

Tenere presente che non è possibile modificare l'assembly fornito dal fornitore.

In sostanza, ecco cosa ho:

Da vendor:

internal class InternalClass 
    public string test; 
end class 

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

Dal mio assembly utilizzando l'assemblaggio fornitore.

public class MyClass 
{ 
    public void AccessTest() 
    { 
    Vendor vendor = new Vendor(); 
    object value = vendor.Tag; 
    // Here I want to access InternalClass.test 
    } 
} 

risposta

70

Senza l'accesso al tipo (e non "InternalsVisibleTo", ecc) si dovrà utilizzare la riflessione. Ma una domanda migliore sarebbe: dovrebbe accedere a questi dati? Non fa parte del contratto di tipo pubblico ... mi sembra che sia inteso come un oggetto opaco (per i loro scopi, non per il tuo).

Lo hai descritto come un campo di istanze pubbliche; per ottenere questo attraverso la riflessione:

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

Se si tratta in realtà di una proprietà (non un campo):

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

Se si tratta di non-pubblico, sarà necessario utilizzare il BindingFlags sovraccarico GetField/GetProperty.

Importante a parte: attenzione con il riflesso come questo; l'implementazione potrebbe cambiare nella prossima versione (rompendo il tuo codice), oppure potrebbe essere offuscata (rompendo il tuo codice), o potresti non avere abbastanza "fiducia" (rottura del tuo codice). Stai individuando il modello?

+1

Wooo .. 2 minuti! Era vicino! Ben detto Marc (come sempre). : D – Galilyou

+0

Grande! Che funzioni. Pensavo di non poter accedere agli interni in questo modo ... Grazie mille –

+0

Marc mi chiedo ... è possibile accedere a campi/proprietà private ma c'è un modo per lanciare l'oggetto restituito da GetValue usando il tipo giusto? – codingadventures

-3

Beh, non è possibile. Le classi interne non possono essere visibili al di fuori del loro assembly, quindi non esiste un modo esplicito per accedervi direttamente -AFAIK ovviamente. L'unico modo è utilizzare l'associazione in fase di runtime mediante riflessione, quindi è possibile richiamare metodi e proprietà dalla classe interna in modo indiretto.

+0

la soluzione di zonkflut ha funzionato per me in passato – phlip

+0

sebbene si basi sull'editing dell'assieme chiamato – phlip

+4

È possibile chiamare qualsiasi cosa con la riflessione –

3

Riflessione.

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

Utilizzare la potenza con saggezza. Non dimenticare il controllo degli errori. :)

171

Vedo solo un caso che consentirebbe l'esposizione dei membri interni a un altro assieme e che è a scopo di test.

dicendo che c'è un modo per consentire assemblee "amico" l'accesso alle parti interne:

Nel file AssemblyInfo.cs del progetto si aggiunge una riga per ogni assemblaggio.

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

questa informazione è disponibile here.

Spero che questo aiuti.

+1

Proprio oggi stavo dando un'occhiata a uno screencst e sentito parlare dell'esposizione dei membri interni di un determinato assieme in modo un altro (il test assemblaggio) ha accesso ad esso, ma non ha idea di come farlo. Grazie! –

+3

Vota perché questa è stata un'informazione utile per me ... anche se questa non può essere la risposta alla domanda :-D – yeyeyerman

+4

la domanda dice esplicitamente che l'assembly del partner non può essere modificato. Votare-down. – danielpops

3

Vorrei discutere un punto: non è possibile aumentare il gruppo originale: utilizzando Mono.Cecil è possibile iniettare [InternalsVisibleTo(...)] nell'assieme 3pty. Nota che potrebbero esserci implicazioni legali - stai scherzando con l'assemblaggio 3pty e le implicazioni tecniche - se l'assembly ha un nome forte, devi spogliarlo o firmarlo di nuovo con una chiave diversa.

Install-Package Mono.Cecil 

E il codice come:

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")) 
    // }); 
} 
+0

Per me, non può essere modificato significa che proviene da un nuget e non voglio dover creare e gestire un nuget locale con le modifiche. Inoltre, per alcune persone, la perdita di un nome forte sarà importante. Ma questo è un punto interessante. – binki