【问题标题】:How can I access an internal class from an external assembly?如何从外部程序集中访问内部类?
【发布时间】:2010-10-29 13:58:32
【问题描述】:

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

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

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

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

来自供应商:

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

【问题讨论】:

    标签: c# class


    【解决方案1】:

    在 .NET 5 中,可以将 InternalsVisibleToAttribute 添加到您的 .csproj:

    <ItemGroup>
        <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
          <_Parameter1>Core.Tests</_Parameter1>
        </AssemblyAttribute>
    </ItemGroup>
    

    【讨论】:

    • 谢谢!这就是我一直在寻找的。​​span>
    【解决方案2】:

    我想争论一点——你不能增加原始程序集——使用 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"))
       // });
    }
    

    【讨论】:

    • 对我来说,不能修改意味着它来自 nuget,我不想通过修改创建和管理本地 nuget。此外,对于某些人来说,失去一个强大的名字也很重要。但这是一个有趣的观点。
    【解决方案3】:

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

    说有一种方法可以让“朋友”程序集访问内部:

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

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

    此信息可用here.

    希望这会有所帮助。

    【讨论】:

    • 扩展测试目的的案例。在不同上下文中耦合内部的任何场景。例如,在 Unity 中,您可能有一个运行时程序集、一个测试程序集和一个编辑器程序集。运行时内部应该对测试和编辑器程序集可见。
    • 我认为这个答案没有帮助 - OP 说他不能修改供应商的程序集,而这个答案谈到修改供应商项目的 AssemblyInfo.cs 文件
    • 要从 Visual Studio 中编辑此信息,请卸载项目,右键单击项目名称,然后单击 Edit &lt;project&gt;.csproj。它将采用 XML 格式,但您可以编辑同样的信息。
    • 我关注了您的链接,但仍然不知道如何实现这一点。您如何从您的 .cs 项目中实际引用包含所有这些内部类的 .dll ?它在文档中提到了朋友,但没有提到路径或如何将它们链接在一起。 "name of your assembly here",如果我们在谈论 DLL 与 CS 项目,它实际上应该包含什么?
    【解决方案4】:

    如果无法访问类型(也没有“InternalsVisibleTo”等),您将不得不使用反射。但更好的问题是:应该您访问这些数据吗?它不是公共类型合同的一部分……在我看来,它应该被视为一个不透明的对象(为了他们的目的,而不是你的目的)。

    您已将其描述为公共实例字段;通过反射得到这个:

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

    如果它实际上是一个属性(不是一个字段):

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

    如果是非公开的,则需要使用GetField/GetPropertyBindingFlags重载。

    重要的一边:小心这样的反思;实现可能会在下一个版本中更改(破坏您的代码),或者它可能被混淆(破坏您的代码),或者您可能没有足够的“信任”(破坏您的代码)。你发现模式了吗?

    【讨论】:

    • Marc 我想知道...可以访问私有字段/属性,但有没有办法使用正确的类型转换 GetValue 返回的对象?
    • @GiovanniCampo 如果您静态地知道类型是什么:当然 - 只是强制转换。如果您不知道静态类型 - 甚至不清楚这将是什么意思
    • 那些将内容发布为真正属于公共 API 的内部组件的人呢?或者他们使用了InternalsVisibleTo,但没有包含您的程序集?如果符号没有真正隐藏,它就是 ABI 的一部分。
    【解决方案5】:

    反射。

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

    明智地使用权力。不要忘记错误检查。 :)

    【讨论】:

      【解决方案6】:

      嗯,你不能。内部类在它们的程序集之外是不可见的,所以没有直接访问它的显式方法——当然是AFAIK。 唯一的方法是通过反射使用运行时后期绑定,然后你可以间接调用内部类的方法和属性。

      【讨论】:

      • 你可以用反射调用任何东西
      猜你喜欢
      • 2011-01-02
      • 2021-12-31
      • 2011-02-13
      • 2021-03-13
      • 1970-01-01
      • 2019-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多