Làm cách nào để truy cập một lớp nội bộ từ một hội đồng bên ngoài?


97

Có một hội đồng mà tôi không thể sửa đổi (do nhà cung cấp cung cấp) có một phương thức trả về một kiểu đối tượng nhưng thực sự là một kiểu nội bộ.

Làm cách nào để truy cập các trường và / hoặc phương thức của đối tượng từ assembly của tôi?

Hãy nhớ rằng tôi không thể sửa đổi lắp ráp do nhà cung cấp cung cấp.

Về bản chất, đây là những gì tôi có:

Từ nhà cung cấp:

internal class InternalClass
  public string test;
end class

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

Từ lắp ráp của tôi bằng cách sử dụng lắp ráp của nhà cung cấp.

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

Câu trả lời:


83

Nếu không có quyền truy cập vào loại (và không có "InternalsVibleTo", v.v.), bạn sẽ phải sử dụng phản chiếu. Nhưng một câu hỏi tốt hơn sẽ được: nên bạn được truy cập vào dữ liệu này? Nó không phải là một phần của hợp đồng loại công khai ... tôi nghe có vẻ như nó được coi như một vật thể không rõ ràng (vì mục đích của họ, không phải của bạn).

Bạn đã mô tả nó như một trường phiên bản công khai; để có được điều này thông qua phản ánh:

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

Nếu nó thực sự là một thuộc tính (không phải một trường):

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

Nếu nó không công khai, bạn sẽ cần sử dụng BindingFlagsquá tải của GetField/ GetProperty.

Quan trọng sang một bên : hãy cẩn thận với những phản ánh như thế này; việc triển khai có thể thay đổi trong phiên bản tiếp theo (phá vỡ mã của bạn) hoặc nó có thể bị xáo trộn (phá vỡ mã của bạn) hoặc bạn có thể không có đủ "niềm tin" (phá vỡ mã của bạn). Bạn có phát hiện ra mô hình không?


Marc Tôi tự hỏi ... có thể truy cập vào các trường / thuộc tính riêng tư nhưng có cách nào để ép đối tượng được trả về bởi GetValue bằng cách sử dụng đúng loại không?
codingadventures

1
@GiovanniCampo nếu bạn biết kiểu tĩnh là gì: chắc chắn - chỉ cần truyền. Nếu bạn không biết kiểu tĩnh - không rõ điều này thậm chí có nghĩa là gì
Marc Gravell

Còn những người xuất bản những thứ thực sự là một phần của API công khai thì sao? Hoặc họ đã sử dụng InternalsVisibleTonhưng không bao gồm lắp ráp của bạn? Nếu biểu tượng không thực sự bị ẩn, nó là một phần của ABI.
binki

208

Tôi chỉ thấy một trường hợp mà bạn cho phép các thành viên nội bộ của mình tiếp xúc với một hội đồng khác và đó là cho mục đích thử nghiệm.

Nói rằng có một cách để cho phép nhóm "Bạn bè" truy cập vào nội bộ:

Trong tệp AssemblyInfo.cs của dự án, bạn thêm một dòng cho mỗi hội.

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

thông tin này có sẵn ở đây.

Hi vọng điêu nay co ich.


Mở rộng trường hợp mục đích thử nghiệm. Bất kỳ tình huống nào mà bạn đã kết hợp nội bộ trong các bối cảnh khác nhau. Ví dụ, trong Unity, bạn có thể có một hội đồng thời gian chạy, một hội đồng thử nghiệm và một hội đồng soạn thảo. Nội bộ thời gian chạy phải được hiển thị để kiểm tra và biên tập hội đồng.
Steve Buzonas

3
Tôi không nghĩ rằng câu trả lời này giúp - OP nói rằng ông không thể sửa đổi lắp ráp của nhà cung cấp, và câu trả lời này cuộc đàm phán về việc sửa đổi tập tin AssemblyInfo.cs của dự án của nhà cung cấp
Simon Xanh

6

Tôi muốn tranh luận một điểm - rằng bạn không thể tăng cường lắp ráp ban đầu - bằng cách sử dụng Mono.Cecil, bạn có thể đưa [InternalsVisibleTo(...)]vào lắp ráp 3pty. Lưu ý rằng có thể có các tác động pháp lý - bạn đang gặp rắc rối với việc lắp ráp 3pty và các hàm ý kỹ thuật - nếu cụm có tên mạnh, bạn cần phải loại bỏ hoặc ký lại bằng khóa khác.

 Install-Package Mono.Cecil

Và mã như:

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
Đối với tôi, không thể sửa đổi có nghĩa là nó đến từ một nuget và tôi không muốn phải tạo và quản lý một nuget cục bộ với các sửa đổi. Ngoài ra, đối với một số người, việc mất đi một cái tên mạnh sẽ có ý nghĩa. Nhưng đây là một điểm thú vị.
binki

3

Suy ngẫm.

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

Sử dụng sức mạnh một cách khôn ngoan. Đừng quên kiểm tra lỗi. :)


-2

Chà, bạn không thể. Các lớp bên trong không thể hiển thị bên ngoài assembly của chúng, vì vậy không có cách nào rõ ràng để truy cập trực tiếp vào nó -AFAIK tất nhiên. Cách duy nhất là sử dụng liên kết trễ thời gian chạy thông qua phản chiếu, sau đó bạn có thể gọi các phương thức và thuộc tính từ lớp bên trong một cách gián tiếp.


5
Bạn có thể gọi bất cứ điều gì với sự phản ánh
MrHinsh - Martin Hinshelwood
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.