为什么在编译时可以用值字符串替换值类型的GetType()?

时间:2017-01-30 16:10:07

标签: c# compiler-optimization

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

我试图更好地处理C#的工作原理,所以这个问题更具理论性而非实际性。

据我了解,在值类型上调用GetType需要装箱,然后调用方法。但是由于值类型不能继承,但是类型在编译时是已知的,那么为什么编译器只能用字符串文字替换对GetType()的调用?

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

3 个答案:

答案 0 :(得分:5)

如果您没有误解GetType返回字符串,请考虑您可能会问过的问题。编译器可以编译

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

as

Type t = typeof(Foo);

是的,这将是一个合法的优化。编译器没有进行这种优化,因为当编译团队进行优化实际上有所作为时,浪费编译团队的时间进行优化。让我们考虑一下拟议的优化。

  • new GetType上是否有Foo方法?如果是这样,那么它可以做任何事情。编译器团队必须检测对原始GetType的调用。然后编写测试用例,确保在这些情况下不应用优化。
  • 如果接收器有任何副作用,则不正确。在这些情况下,编译器团队必须检测副作用并抑制优化,或者以保留副作用的方式实现优化。再次编写测试用例。
  • 在我们有Foo?而不是Foo的情况下,这些副作用包括可能的空取消引用异常,因此您必须在编译器中具有特殊情况。< / LI>
  • 可能是值类型的泛型上的GetType怎么样?有许多案例需要考虑,同样,这些案例会增加优化的设计,实施和测试成本。
  • 优化可以节省一次拳击惩罚。假设的代码即将进行不必要的反思。这是否会使您成为应用程序实现高性能的关键路径上的代码?不要消除拳击;消除反射!
  • 优化首先没有人写入的代码的优化不是一个有用的优化。为什么要进行反射以确定您已经知道编译时类型的表达式的类型?明智的人不会首先编写此代码,因此无需对其进行优化。
  • 或换句话说:如果你不小心以产生拳击转换的方式编写代码,而你想要消除它,你可以这么做。当你很容易做到时,编译器不需要为你做这件事。

因此,出于所有这些原因,优化成本高于其产生的效益。

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

答案 1 :(得分: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!

iKnowTheTypes相同的字符串,这意味着nameof(System.Int32)和文字"Int32"基本相同(请参阅{ {3}}有关此主题的更准确信息。)

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

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

答案 2 :(得分:1)

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

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