ฉันจะเข้าถึงคลาสภายในจากแอสเซมบลีภายนอกได้อย่างไร


100

มีแอสเซมบลีที่ฉันไม่สามารถแก้ไขได้ (จัดหาโดยผู้จำหน่าย) ซึ่งมีวิธีการส่งคืนประเภทอ็อบเจ็กต์แต่เป็นประเภทภายในจริงๆ

ฉันจะเข้าถึงฟิลด์และ / หรือวิธีการของวัตถุจากแอสเซมบลีของฉันได้อย่างไร

โปรดทราบว่าฉันไม่สามารถแก้ไขชุดประกอบที่จัดหาโดยผู้จัดจำหน่ายได้

โดยพื้นฐานแล้วนี่คือสิ่งที่ฉันมี:

จากผู้ขาย:

internal class InternalClass
  public string test;
end class

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

จากการประกอบของฉันโดยใช้แอสเซมบลีของผู้ขาย

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

คำตอบ:


85

หากไม่สามารถเข้าถึงประเภท (และไม่มี "InternalsVisibleTo" ฯลฯ ) คุณจะต้องใช้การสะท้อน แต่คำถามที่ดีกว่าคือคุณควรเข้าถึงข้อมูลนี้หรือไม่? มันไม่ได้เป็นส่วนหนึ่งของสัญญาประเภทสาธารณะ ... สำหรับฉันดูเหมือนว่าตั้งใจจะถูกมองว่าเป็นวัตถุทึบแสง (เพื่อวัตถุประสงค์ของพวกเขาไม่ใช่ของคุณ)

คุณได้อธิบายว่าเป็นฟิลด์อินสแตนซ์สาธารณะ เพื่อรับสิ่งนี้ผ่านการสะท้อน:

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

หากเป็นคุณสมบัติจริง (ไม่ใช่ฟิลด์):

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

ถ้ามันไม่เป็นสาธารณะคุณจะต้องใช้BindingFlagsเกินพิกัดของ/GetFieldGetProperty

สิ่งสำคัญนอกเหนือจากนี้ : ระวังการสะท้อนเช่นนี้ การใช้งานอาจเปลี่ยนแปลงได้ในเวอร์ชันถัดไป (ทำลายโค้ดของคุณ) หรืออาจทำให้สับสน (ทำลายโค้ดของคุณ) หรือคุณอาจมี "ความไว้วางใจ" ไม่เพียงพอ (ทำลายโค้ดของคุณ) คุณสังเกตเห็นรูปแบบหรือไม่?


Marc ฉันสงสัยว่า ... เป็นไปได้ที่จะเข้าถึงฟิลด์ / คุณสมบัติส่วนตัว แต่มีวิธีการส่งคืนวัตถุโดย GetValue โดยใช้ประเภทที่ถูกต้องหรือไม่?
codingadventures

1
@GiovanniCampo ถ้าคุณรู้แบบคงที่ว่าเป็นประเภทใด: แน่นอน - เพียงแค่โยน หากคุณไม่ทราบประเภทคงที่ - ไม่ชัดเจนว่านี่จะหมายถึงอะไร
Marc Gravell

แล้วคนที่เผยแพร่สิ่งต่าง ๆ เป็นภายในซึ่งเป็นส่วนหนึ่งของ API สาธารณะจริงๆล่ะ? หรือพวกเขาใช้InternalsVisibleToแต่ไม่รวมการประกอบของคุณ? หากสัญลักษณ์ไม่ได้ถูกซ่อนไว้จริงๆแสดงว่าเป็นส่วนหนึ่งของ ABI
binki

211

ฉันเห็นเพียงกรณีเดียวที่คุณอนุญาตให้เปิดเผยสมาชิกภายในของคุณในการประชุมอื่นและนั่นคือเพื่อการทดสอบ

การบอกว่ามีวิธีอนุญาตให้กลุ่ม "เพื่อน" เข้าถึงภายในได้

ในไฟล์ AssemblyInfo.cs ของโปรเจ็กต์คุณจะเพิ่มบรรทัดสำหรับแต่ละแอสเซมบลี

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

ข้อมูลนี้มีอยู่ที่นี่

หวังว่านี่จะช่วยได้


ขยายความในกรณีของวัตถุประสงค์ในการทดสอบ สถานการณ์ใด ๆ ที่คุณเชื่อมโยงภายในในบริบทที่แตกต่างกัน ตัวอย่างเช่นใน Unity คุณอาจมีแอสเซมบลีรันไทม์ชุดทดสอบและแอสเซมบลีตัวแก้ไข ภายในรันไทม์ควรมองเห็นได้เพื่อทดสอบและแก้ไขแอสเซมบลี
Steve Buzonas

4
ฉันไม่คิดว่าคำตอบนี้จะช่วยได้ - OP บอกว่าเขาไม่สามารถแก้ไขชุดประกอบของผู้ขายได้และคำตอบนี้พูดถึงการแก้ไขไฟล์ AssemblyInfo.cs ของโครงการของผู้ขาย
Simon Green

6

ฉันขอโต้แย้งประเด็นหนึ่ง - ว่าคุณไม่สามารถเพิ่มแอสเซมบลีเดิมได้ - โดยใช้ Mono.Cecil คุณสามารถฉีด[InternalsVisibleTo(...)]ไปยังแอสเซมบลีที่ว่าง 3 อัน โปรดทราบว่าอาจมีผลทางกฎหมาย - คุณกำลังยุ่งอยู่กับการประกอบที่ว่างเปล่า 3 ชุดและผลกระทบทางเทคนิค - หากชุดประกอบมีชื่อที่รัดกุมคุณอาจต้องถอดหรือเซ็นชื่อใหม่ด้วยคีย์อื่น

 Install-Package Mono.Cecil

และรหัสเช่น:

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

1
สำหรับฉันไม่สามารถแก้ไขได้หมายความว่ามันมาจากนักเก็ตและฉันไม่ต้องการที่จะสร้างและจัดการนักเก็ตในท้องถิ่นด้วยการปรับเปลี่ยน นอกจากนี้สำหรับบางคนการสูญเสียชื่อที่แข็งแกร่งจะมีความสำคัญ แต่นี่เป็นจุดที่น่าสนใจ
binki

3

การสะท้อนกลับ.

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

ใช้พลังอย่างชาญฉลาด อย่าลืมตรวจสอบข้อผิดพลาด :)


-2

ดีคุณทำไม่ได้ ชั้นเรียนภายในไม่สามารถมองเห็นได้ภายนอกการชุมนุมดังนั้นจึงไม่มีวิธีที่ชัดเจนในการเข้าถึงโดยตรง -AFAIK แน่นอน วิธีเดียวคือใช้ runtime late-binding ผ่านการสะท้อนจากนั้นคุณสามารถเรียกใช้เมธอดและคุณสมบัติจากคลาสภายในโดยอ้อม


5
คุณสามารถเรียกอะไรก็ได้ด้วยการไตร่ตรอง
MrHinsh - Martin Hinshelwood
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.