C# - 如何从外部程序集访问内部类

时间:2022-09-02 09:16:47

Having an assembly which I cannot modify (vendor-supplied) which have a method returning an object type but is really of an internal type.

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

How can I access the fields and/or methods of the object from my assembly?

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

Keep in mind that I cannot modify the vendor-supplied assembly.

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

In essence, here's what I have:

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

From vendor:

来自供应商:

internal class InternalClass
  public string test;
end class

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

From my assembly using the vendor assembly.

从我使用供应商组件的装配。

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

5 个解决方案

#1


74  

Without access to the type (and no "InternalsVisibleTo" etc) you would have to use reflection. But a better question would be: should you be accessing this data? It isn't part of the public type contract... it sounds to me like it is intended to be treated as an opaque object (for their purposes, not yours).

如果没有访问类型(并且没有“InternalsVisibleTo”等),则必须使用反射。但更好的问题是:你应该访问这些数据吗?它不是公共类型合同的一部分...它听起来像是打算被视为一个不透明的对象(为了他们的目的,而不是你的目的)。

You've described it as a public instance field; to get this via reflection:

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

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

If it is actually a property (not a field):

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

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

If it is non-public, you'll need to use the BindingFlags overload of GetField/GetProperty.

如果它是非公共的,则需要使用GetField / GetProperty的BindingFlags重载。

Important aside: be careful with reflection like this; the implementation could change in the next version (breaking your code), or it could be obfuscated (breaking your code), or you might not have enough "trust" (breaking your code). Are you spotting the pattern?

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

#2


182  

I see only one case that you would allow exposure to your internal members to another assembly and that is for testing purposes.

我只看到一个案例,你允许你的内部成员接触另一个组件,这是为了测试目的。

Saying that there is a way to allow "Friend" assemblies access to internals:

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

In the AssemblyInfo.cs file of the project you add a line for each assembly.

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

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

this info is available here.

此信息可在此处获得。

Hope this helps.

希望这可以帮助。

#3


3  

Reflection.

反射。

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

Use the power wisely. Don't forget error checking. :)

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

#4


3  

I would like to argue one point - that you cannot augment the original assembly - using Mono.Cecil you can inject [InternalsVisibleTo(...)] to the 3pty assembly. Note there might be legal implications - you're messing with 3pty assembly and technical implications - if the assembly has strong name you either need to strip it or re-sign it with different key.

我想争论一点 - 你无法扩充原始程序集 - 使用Mono.Cecil你可以将[InternalsVisibleTo(...)]注入3pty程序集。请注意,可能存在法律含义 - 您正在搞乱3pty汇编和技术含义 - 如果程序集具有强名称,您需要将其删除或使用不同的密钥重新签名。

 Install-Package Mono.Cecil

And the code like:

代码如下:

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

#5


-3  

Well, you can't. Internal classes can't be visible outside of their assembly, so no explicit way to access it directly -AFAIK of course. The only way is to use runtime late-binding via reflection, then you can invoke methods and properties from the internal class indirectly.

好吧,你不能。内部类在其程序集之外是不可见的,因此没有直接访问它的明确方法-AFAIK当然。唯一的方法是通过反射使用运行时后期绑定,然后您可以间接地从内部类调用方法和属性。

#1


74  

Without access to the type (and no "InternalsVisibleTo" etc) you would have to use reflection. But a better question would be: should you be accessing this data? It isn't part of the public type contract... it sounds to me like it is intended to be treated as an opaque object (for their purposes, not yours).

如果没有访问类型(并且没有“InternalsVisibleTo”等),则必须使用反射。但更好的问题是:你应该访问这些数据吗?它不是公共类型合同的一部分...它听起来像是打算被视为一个不透明的对象(为了他们的目的,而不是你的目的)。

You've described it as a public instance field; to get this via reflection:

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

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

If it is actually a property (not a field):

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

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

If it is non-public, you'll need to use the BindingFlags overload of GetField/GetProperty.

如果它是非公共的,则需要使用GetField / GetProperty的BindingFlags重载。

Important aside: be careful with reflection like this; the implementation could change in the next version (breaking your code), or it could be obfuscated (breaking your code), or you might not have enough "trust" (breaking your code). Are you spotting the pattern?

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

#2


182  

I see only one case that you would allow exposure to your internal members to another assembly and that is for testing purposes.

我只看到一个案例,你允许你的内部成员接触另一个组件,这是为了测试目的。

Saying that there is a way to allow "Friend" assemblies access to internals:

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

In the AssemblyInfo.cs file of the project you add a line for each assembly.

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

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

this info is available here.

此信息可在此处获得。

Hope this helps.

希望这可以帮助。

#3


3  

Reflection.

反射。

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

Use the power wisely. Don't forget error checking. :)

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

#4


3  

I would like to argue one point - that you cannot augment the original assembly - using Mono.Cecil you can inject [InternalsVisibleTo(...)] to the 3pty assembly. Note there might be legal implications - you're messing with 3pty assembly and technical implications - if the assembly has strong name you either need to strip it or re-sign it with different key.

我想争论一点 - 你无法扩充原始程序集 - 使用Mono.Cecil你可以将[InternalsVisibleTo(...)]注入3pty程序集。请注意,可能存在法律含义 - 您正在搞乱3pty汇编和技术含义 - 如果程序集具有强名称,您需要将其删除或使用不同的密钥重新签名。

 Install-Package Mono.Cecil

And the code like:

代码如下:

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

#5


-3  

Well, you can't. Internal classes can't be visible outside of their assembly, so no explicit way to access it directly -AFAIK of course. The only way is to use runtime late-binding via reflection, then you can invoke methods and properties from the internal class indirectly.

好吧,你不能。内部类在其程序集之外是不可见的,因此没有直接访问它的明确方法-AFAIK当然。唯一的方法是通过反射使用运行时后期绑定,然后您可以间接地从内部类调用方法和属性。