为什么 Object.GetType() 是一个方法而不是属性?

13

从设计角度来看,我想知道.NET的创建者为什么选择了System.Object.GetType()而不是一个System.Object.Type只读属性。

这只是一个(非常微小的)设计缺陷,还是有些原理在其中?欢迎任何解释。


2
+1 棒极了的问题。总结一下,1)要保持一致,因为 GetType 可以重载(由 Philippe 提出),2)因为属性不能抛出异常(由 Mikko 提出),3)因为属性不能太昂贵(由 Joel 提出),4)出于性能考虑(由 Martin 提出)。对他们所有人都加一分。 - nawfal
@nawfal 属性可以抛出异常。举个例子,Nullable<T>。有趣的是,当HasValuefalse时,调用Value属性会引发异常。 - Mr Anderson
@MrAnderson 我表达得不太对。我的意思是属性最好不要抛出异常,这只是一个好习惯,而不是一条规则。如果有必要,属性可以和应该抛出异常。 - nawfal
5个回答

17

如果你在反编译器Reflector中查看GetType()的声明,你会发现这个:

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

attribute 和 extern 这样的组合意味着该方法实际上是在 .NET 运行时内部的未托管代码中实现的。 这篇文章 中的 GUID 问题进一步阐述了这一点。他们显然之所以这样做是出于性能原因,经过确定,如果在较低的级别处理,找出 Type 会更快。

这导致了两个不实现 GetType 方法作为属性的原因。首先,无法像方法一样定义属性 extern,因此需要在本机 .NET 代码中处理它。其次,即使您可以将它们定义为 extern,从属性内部执行不安全的、未托管的调用肯定会违反属性用法指南,因为更难以保证没有副作用。


1
这个答案是错误的。属性可以声明为extern。下面这个例子是完全有效的:public extern Type Type { [MethodImplAttribute(MethodImplOptions.InternalCall)]get; } - Mr Anderson

7
指南指出,属性应该代表对象的状态,不应在性能方面昂贵,并且除了计算/设置该状态之外不应具有副作用。我猜GetType()不遵守这些规则,所以它被制作成了一个方法。
GetType()是一个稍微昂贵的操作。如果它是一个属性,那么它会鼓励像这样的使用:
DoStuff(obj.Type);
....
DoStuff(obj.Type);

etc.

instead of

Type type = obj.GetType();
DoStuff(type);
....
DoStuff(type); 

这样做不是很优秀,所以他们把它作为一种方法来建议尽量少用。


6
我宁愿看到原始代码。首先优化可读性。当你说“稍微有点贵”的时候,我们真正讨论的是多贵?在我的NC10笔记本电脑上,对GetType()进行1000万次调用大约需要200毫秒。这比一个只同步然后返回私有变量的属性要快得多。(不过,一个简单的属性还更快,必须承认...)我不认为这是将其作为方法的好理由。 - Jon Skeet
2
对于没有先进行测量而导致的错误,我深感抱歉。但我们需要记住,该方法从1.0版本就存在了,那时可能会更慢。这只是我的个人看法。无论如何 - 我刚刚被“skeeted” :)。而这是一件好事。 - Adrian Zanescu
4
Jon Skeet说,GetType执行所需的时间取决于您加载的程序集数量。我现在正在优化一些代码,在我的情况下,8个调用需要约600毫秒,这很多。 - Roman Royter
1
@stakx:我认为,只有在 AppDomain 被卸载之前,“Type”对象才不会被垃圾回收,所以从这些方面来看没有任何区别。您有任何迹象表明类型确实被垃圾回收了吗? - Jon Skeet
1
@JonSkeet:我的上面的评论只是基于Jeffrey Richter对CLR的了解而言。来自《C# via CLR》第三版,第658页:“TypeMemberInfo派生对象需要大量内存。因此,如果应用程序持有太多这些对象……,应用程序的内存消耗会急剧增加,对应用程序的性能产生不利影响。在内部,CLR有一种更紧凑的方式来表示这些信息。CLR仅为我们的应用程序创建这些对象,以使开发人员更容易使用。”(还有更多内容,但现在就让它们足够了。) - stakx - no longer contributing
显示剩余7条评论

3

据我所了解,通常认为将内部字段或值通过属性公开是良好的实践,而其他可能需要更多时间或其他资源来计算的值则应该使用方法来公开。


3

只有微软能回答这个问题,但我认为这是因为.NET框架中的几个类创建了自己重载版本的GetType()方法,并增加了额外的参数。如果它是一个属性,它们无法使用相同的名称(因为属性没有参数)。

这只是我的一些想法。


2

有点晚回复,但在寻找相关内容时遇到了这个问题。

GetType 可能会抛出异常。框架指南说明属性不应该引发异常。这是将其作为方法而不是属性的另一个原因。


根据文档,GetType() 不会抛出任何异常。 - Serge Wautier
1
如果在 Nullable<T> 上调用 GetType(),并且 HasValue 字段为 null,则会引发异常。当 GetType() 被发明时(因为 Nullable<T> 不存在),这不是问题,但如果 GetType 是一个属性,那么这将是一种奇怪的行为(因为 Nullable<T> 实际上从来不是 null,并且具有像 HasValue 这样总是正常工作的属性)。 - supercat
1
supercat,我猜你的意思是Value可能为null?HasValue只是一个布尔值,true或false,永远不会为null。 - Concrete Gannet
@ConcreteGannet:我的意思是HasValue字段为false的那个。两个字段都不能为null。 - supercat
@MrAnderson:在没有执行与该方法相关联的任何代码的情况下,调用值类型变量上的实例方法可能会产生NullReferenceException的其他实例吗?与其定义仅适用于一个方法的规则并说行为是“规则”的结果而不是方法,我认为将行为与方法相关联更有帮助。 - supercat
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接