【问题标题】:Why can't GetType() on a value type be replaced with a string literal at compile time?为什么不能在编译时将值类型上的 GetType() 替换为字符串文字?
【发布时间】:2017-01-30 16:10:07
【问题描述】:

编辑这个问题是基于 GetType() 返回字符串的误解。

我试图更好地了解 C# 的工作原理,所以这个问题更多的是理论而不是实际。

据我了解,对值类型调用 GetType 需要装箱,然后调用该方法。既然值类型不能被继承,但是类型在编译时是已知的,那么为什么编译器不能简单地用字符串字面量替换对 GetType() 的调用呢?

或者这是可以完成的事情,但被认为没有必要,因为无论如何都不需要在未装箱的值类型上调用 GetType?

【问题讨论】:

  • GetType 不返回字符串文字,因此您显然不能用字符串文字替换它。
  • 我明白了——我正在查看的源代码声称 GetType() 返回一个字符串,这是错误的;它返回实际的 Type 对象。这是在运行时在堆上分配的东西,因此无法在编译时对其进行优化。谢谢!
  • Since value types can't be inherited from, though, the type is known at compile time 也不正确。它经常知道,但并不总是知道。
  • @Servy:需要详细说明吗?
  • 如果你想要这个:使用nameof(Foo) 而不是foo.GetType().Nametypeof(Foo).Name

标签: c# compiler-optimization


【解决方案1】:

如果您没有误解 GetType 返回一个字符串,那么让我们考虑一下您可能会问的问题。编译器能编译吗

Foo foo = whatever;
Type t = foo.GetType();

作为

Type t = typeof(Foo);

是的,那将是合法的优化。编译器不进行该优化,因为当编译器团队可以进行实际上产生影响的优化时,进行该优化将浪费编译器团队的时间。让我们考虑一下建议的优化。

  • Foo 上是否有 new GetType 方法?如果是这样,那么它可以做任何事情。编译器团队必须检测对原始GetType 的调用。然后编写测试用例,确保优化不适用于这些用例。
  • 如果接收器有任何副作用是不正确的。编译器团队必须检测副作用并在这些情况下抑制优化,或者以保留副作用的方式实施优化。再次编写测试用例。
  • 这些副作用包括在我们使用 Foo? 而不是 Foo 的情况下可能出现的 null 取消引用异常,因此您必须在编译器中有一个特殊情况。
  • 对于可能是值类型的泛型,GetType 怎么样?那里有很多案例需要考虑,而且这些案例也会增加优化的设计、实施和测试成本。
  • 优化节省了一次装箱惩罚。假设的代码即将进行不必要的反思。您是否觉得这是应用程序实现高性能的关键路径上的代码?不要消除拳击;消除反射!
  • 优化一开始就没有人编写的代码的优化不是有用的优化。为什么要进行反射以确定在编译时已经知道类型的表达式的类型?懂事的人一开始不会写这段代码,所以没必要优化。
  • 或者换一种说法:如果您不小心以产生装箱转换的方式编写代码,并且想要消除它,您可以轻松地做到这一点。当您自己轻松完成时,编译器无需为您完成。

因此,出于所有这些原因以及更多原因,优化的成本高于其产生的收益。

有关如何评估建议的优化的更长但类似的讨论,请参阅昨天的答案:Weird behaviour of c# compiler due caching delegate

【讨论】:

    【解决方案2】:

    在未装箱的值类型中,GetType始终返回变量的类型。您已经知道变量的类型,那么一开始有什么好处呢?如果您想要名称,只需在类型上使用 nameof

    var i = 1;
    var iKnowTheType = nameof(System.Int32); //is this evaluated at compile time?
    var s = "Int32";
    var areSame = ReferenceEquals(iKnowTheType, s); //returns true!
    

    iKnowTheTypessame 字符串,这意味着 nameof(System.Int32) 和文字 "Int32" 基本上是同一件事(阅读 string interning 以获得更准确的信息主题)。

    GetType 返回对象的 runtime 类型。在未装箱的值类型中,它始终是变量的类型,但这里的关键区别在于该类型是在运行时评估的:

    var i = 1;
    var iDontKnowTheType = i.GetType().Name;
    var s = "Int32";
    var areSame = ReferenceEquals(iDontKnowTheType, s); //returns false!
    var areEqual = iDontKnowTheType  == s; //returns true
    

    在这里,编译器不能实习 iDontKnowTheType,因为特定的 string 是在运行时评估的。

    【讨论】:

      【解决方案3】:

      GetType() 返回一个Type 对象。该对象在编译时不存在,仅在运行时存在。

      此外,该对象还附加了许多元数据和其他运行时数据,因此它不仅可以用于“类型名比较”,还可以用于反射和序列化。

      【讨论】:

        猜你喜欢
        • 2018-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-09
        • 2014-08-10
        • 2010-12-16
        • 2021-03-28
        相关资源
        最近更新 更多