当我出于好奇对所有类型进行反思以检查其他内容时,我偶然发现了一个奇怪的事情。
为什么程序集mscorlib.dll
的类System.__ComObject
(有时?)声称是公共的,而实际上它似乎是非公共的?如果我在一个简单的C#控制台应用程序中运行以下代码:
var t = Type.GetType("System.__ComObject");
Console.WriteLine(t.IsPublic); // "True" ?!
Console.WriteLine(t.IsVisible); // "False"
输出似乎有冲突。非嵌套类型(t.IsNested
为false)应该为IsPublic
和IsVisible
提供相同的真值。当我看带有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修饰符的类型进行比较时,IsPublic
和IsVisible
都会按预期返回false。
有人知道Type.GetType("System.__ComObject").IsPublic
为什么(以及何时)成立吗?
Mono对__ComObject
的实现可能会提供一些线索。它确实被声明为内部,但评论说"它没有公共方法,它的功能是通过System.Runtime.InteropServices.Marshal
暴露的"。我还没有深入研究Marshal
,但我认为它负责GetType()
的实现,所以它也可以自定义IsPublic
属性。
根据我的经验,Type.GetType("System.__ComObject").IsPublic
永远是true
。关于GetType("System.Net.Mail.MSAdminBase")
,我认为它是一个通过自定义的主互操作程序集公开的COM类,在其中可以显式控制类型的可见性(尽管这只是我的想法,但还没有进行任何研究)。
[更新]
我得到了最新的Framework源代码,发现我错误地认为__ComObject
类型的IsPublic
属性的自定义是由Marshal
完成的。实际上,它是由非托管代码完成的。Object.GetType()
在Object.cs中定义如下:
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern Type GetType();
其在.NET 4.x中实现的非托管源代码不可用,但可用于.NET 2.0。关于Object.GetType
在.NET 2.0中的实现,有一个很好的答案。我只想补充一点,IsPublic
是由System.Type
派生的System.Runtime.InteropServices._Type
接口定义的,并且可以被任何System.Type
派生类覆盖。显然,这样的类的实现是由Object.GetType
返回的,这发生在ObjectNative::GetClass
(sscli20\clr\src\vm\cobject.cpp)内部,如答案所示:
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
实例的唯一方法是通过interop。