【问题标题】:Why does the type System.__ComObject claim (sometimes) to be public when it is not?为什么类型 System.__ComObject 声称(有时)不是公开的?
【发布时间】:2023-03-26 15:40:01
【问题描述】:

当我出于好奇对所有类型进行反思以检查其他内容时,碰巧发现了一个奇怪的现象。

为什么程序集mscorlib.dll 的类System.__ComObject(有时?)声称是公开的,而实际上它似乎是非公开的?如果我在一个简单的 C# 控制台应用程序中运行以下代码:

var t = Type.GetType("System.__ComObject");
Console.WriteLine(t.IsPublic);   // "True"   ?!
Console.WriteLine(t.IsVisible);  // "False"

输出似乎有冲突。非嵌套类型(t.IsNested 为假)应该为IsPublicIsVisible 提供相同的真值。当我使用IL DASM 查看程序集时,我看到了:

.class private auto ansi beforefieldinit System.__ComObject
       extends System.MarshalByRefObject
{
} // end of class System.__ComObject

在我看来,它看起来很像一个 非公共 类,它对应于下面的 C# 代码:

namespace System
{
    // not public
    internal class __ComObject : MarshalByRefObject
    {
        ...
    }
}

当我与另一个具有相似名称 System.__Canon 和相似 IL 修饰符的类型进行比较时,IsPublicIsVisible 都按预期返回 false。

有人知道为什么(以及何时)Type.GetType("System.__ComObject").IsPublic 给出了 true 吗?

【问题讨论】:

  • System.__ComObject 和 System.__Canon 非常不同 - 第一个是 COM 对象(IsCOMObject==true) - 这就是为什么我认为它是 Public 而第二个不是。
  • @Mihail 如果我使用var t = typeof(Uri).Assembly.GetType("System.Net.Mail.MSAdminBase");,我还有另一种类型t,它也是IsCOMObject,但IsNotPublic。所以这似乎是该解释的“反例”,除非这取决于程序集(System.Net.Mail.MSAdminBase 在另一个程序集中,与System.__ComObject 相比)。
  • 我所能想到的是,这可能是一个缺陷!我遇到了一些类似的东西,可能你们也遇到了同样的事情。关注link
  • @Nilesh 看起来该链接描述了相同的问题(或包含我的问题和其他问题的问题)。不幸的是,那里没有解释。这种情况似乎六年都没有改变。如果它是一个错误,那就太奇怪了。但是一个非常有趣的评论!
  • 祝贺 Noseratio 在赏金到期后,他的回答自动给了他一些声誉。但是,我仍然觉得我的答案没有得到答复。从技术上讲,IsPublic 通过调用抽象GetAttributeFlagsImpl 起作用,但这并不能帮助我理解__ComObject 的特别之处。我不知道这种行为是必要的和设计的,还是一个错误,我不知道为什么__ComObject 与这里的其他类型不同会很有趣。

标签: c# .net reflection encapsulation cil


【解决方案1】:

Mono's implementation__ComObject 可能会提供一些线索。它确实被声明为 internal,但是 cmets 说 “它没有公共方法,它的功能通过 System.Runtime.InteropServices.Marshal 暴露出来”。我没有深入研究Marshal,但我认为它负责GetType()的实现,因此它也可以自定义IsPublic属性。

根据我的经验,Type.GetType("System.__ComObject").IsPublic 始终是true。关于GetType("System.Net.Mail.MSAdminBase"),我相信它是一个通过customized primary interop assembly 暴露的COM 类,其中可以显式控制类型的可见性(尽管这只是我的想法,尚未进行任何研究)。

[更新]

我得到了latest Framework source code,发现我的假设是错误的,即__ComObject 类型的IsPublic 属性的自定义是由Marshal 完成的。实际上,它是由非托管代码完成的。 Object.GetType() 在 Object.cs 中定义如下:

[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern Type GetType(); 

它在 .NET 4.x 中实现的非托管源代码不可用,但它是 .NET 2.0 的 available。在 .NET 2.0 中有一个关于 Object.GetType 实现的 excellent answer。我要补充一点,IsPublicSystem.Runtime.InteropServices._Type 接口定义,System.Type 派生自该接口,并且可以被任何System.Type-descendent 类覆盖。显然,Object.GetType 返回了此类的一个实现,这发生在ObjectNative::GetClass (sscli20\clr\src\vm\comobject.cpp) 内部,如the answer 所示:

if (!objRef->IsThunking())
    refType = typeHandle.GetManagedClassObject();
else
    refType = CRemotingServices::GetClass(objRef);

我确认IsPublic 的行为取决于框架版本。我在不同的虚拟机中尝试了以下 PowerShell 脚本:

Write-Host ([System.Environment]::Version) ([Type]::GetType("System.__ComObject")).IsPublic

输出是:

2.0.50727.3643 False (WinXP)
4.0.30319.18052 True (Win7)
4.0.30319.19079 True (Win8)

显然,自 .NET 2.0 以来,它已从 False 更改为 True。我同意True__ComObject (internal class __ComObject ...) 的声明不一致。我个人认为没有理由进行这种更改,因为获取__ComObject 实例的唯一方法是通过互操作。

【讨论】:

  • 我没有在 Mono 中测试过这个。我没有在您链接的源代码中找到任何对我来说似乎相关的内容。我不确定您对GetTypeIsPublic 的意思。后者是抽象 System.Type 类上的非虚拟(尽管接口实现)实例属性,所以我需要解释 __ComObjectMarshal 如何“自定义”它(你的话)。我在问题中说“(有时)”的原因是,我在两台不同的机器上在 PowerShell 中对此进行了测试,其中一台机器(通过 PowerShell)__ComObject 显然是非公开的。
  • 我用 PowerShell 尝试了以下操作:Write-Host ([System.Environment]::Version) ([Type]::GetType("System.__ComObject")).IsPublic,输出:4.0.30319.19079 True(Win8)、4.0.30319.18052 True (Win7)、2.0.50727.3643 False (WinXP)。显然,IsPublic__ComObject 的结果取决于框架版本。我会做一些研究并在您要求的Marshal 上提供解释。
  • 抱歉,我不知道您所说的 (IsPublic) 是抽象 @987654372 上的非虚拟(虽然实现了接口)实例属性是什么意思@。该属性在System.Runtime.InteropServices._Type 接口中定义,因此根据定义它是虚拟的,可以被任何派生类覆盖,与IsCOMObject 相同。 System.Type 没有实现 IsPublic,但它的一些派生类实现了,例如System.RuntimeType。我承认我错了,假设定制是由Marshal 完成的,它实际上是用非托管代码完成的。我已经用更多细节更新了我的答案。
  • 关于 PowerShell,我看到了类似的行为。在 __ComObject 似乎是公开的机器上,您实际上可以直接在 PowerShell 中使用它。例如[__ComObject] 将为类提供Type 对象。并且[__ComObject]$x = $null 将声明此类型的变量$x,然后$x = 'Hello' 将失败,因为字符串不适合__ComObject 变量。所以它真的在那里“公开”行事。
  • 关于IsPublic,我说的是this member of System.Type。正如您在该页面上看到的,在 C# 术语中,此属性是非虚拟的。但是,在 Visual C++ 中,术语可能会有所不同。查看关联的get 访问器的CIL,get_IsPublic 方法,我们看到它在该术语中被声明为virtual final。这是因为它实现了接口_Type 的成员。但是 virtual-final(或 virtual-sealed)仍然意味着它可以以任何普通方式被覆盖。
猜你喜欢
  • 1970-01-01
  • 2016-02-02
  • 1970-01-01
  • 2018-06-05
  • 1970-01-01
  • 2018-06-20
  • 2014-08-29
  • 1970-01-01
相关资源
最近更新 更多