如何从外部程序集访问内部类?


97

有一个我无法修改的程序集(供应商提供),该程序集的方法返回对象类型,但实际上是内部类型。

如何从程序集中访问对象的字段和/或方法?

请记住,我无法修改供应商提供的程序集。

本质上,这就是我所拥有的:

来自供应商:

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
  }
}

Answers:


83

如果无法访问类型(并且没有“ 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,如果您静态地知道类型是什么:肯定-只是强制转换。如果你不知道类型静态-目前还不清楚这甚至会平均
马克·Gravell

那些将内容发布为内部组件的人又如何呢?还是他们曾经使用过InternalsVisibleTo但不包含您的程序集?如果该符号没有真正隐藏,则它是ABI的一部分。
binki

208

我只看到一种情况,您可以将内部成员暴露给另一个程序集,这是出于测试目的。

说有一种方法允许“朋友”程序集访问内部:

在项目的AssemblyInfo.cs文件中,为每个程序集添加一行。

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

此信息可在此处获得。

希望这可以帮助。


在测试目的的情况下扩展。您在不同上下文中耦合内部的任何情况。例如,在Unity中,您可能具有运行时程序集,测试程序集和编辑器程序集。运行时内部结构对于测试和编辑器程序集应该是可见的。
Steve Buzonas

3
我认为此答案无济于事-OP表示他无法修改供应商的程序集,而该答案涉及修改供应商项目的AssemblyInfo.cs文件
Simon Green

6

我想说一点-您不能扩充原始程序集-使用Mono.Cecil可以注入[InternalsVisibleTo(...)]3pty程序集。请注意,这可能会涉及法律问题-您可能会混淆3pty程序集和技术方面的含义-如果程序集具有很强的名称,则需要剥离它或使用其他密钥对其重新签名。

 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
对我而言,无法修改意味着它来自nuget,并且我不想通过修改来创建和管理本地nuget。此外,对于某些人来说,失去好名声也很重要。但这是一个有趣的观点。
宾基

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。唯一的方法是通过反射使用运行时后期绑定,然后可以从内部类间接调用方法和属性。


5
您可以通过反射调用任何东西
MrHinsh-Martin Hinshelwood
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.