【问题标题】:Does compile-time type still exist at runtime? [closed]编译时类型在运行时是否仍然存在? [关闭]
【发布时间】:2021-10-06 22:24:38
【问题描述】:

Dog为继承自Animal类的类,我们可以如下进行实例化:

Animal a = new Dog();

我知道在编译时,编译器只知道我们将a 声明为Animal,而在运行时,我们将a 指向Dog 对象。但在这一点上我有些模糊:

编译时类型Animal 是堆栈上变量a 的类型吗?还是只是在编译时告诉编译器a可以引用什么类型的对象,与栈上变量a的类型无关?


我问这个是因为我想知道关于编译时类型 Animal 的信息在运行时是否仍然存在,或者它是否会变得完全不相关并被丢弃?如果还存在,是否作为变量a的类型存储在栈上?如果是这样,当我们引用a的类型时,程序怎么知道我们引用的是a引用的对象的类型,还是栈上变量a的类型?

我是这个概念的新手,可能会有一些误解。谁能为我澄清一下?提前致谢!

【问题讨论】:

  • 您可以通过a.GetType() 尝试检查对象的运行时类型
  • 对于局部变量,我不确定。但是对于字段,编译时类型肯定是在运行时保留的。你可以通过反射得到它。
  • @Fabio 谢谢你的评论 :) 所以我可以问一下堆栈上的变量a(不是它在堆上引用的对象)在运行时的类型是Dog ?
  • @J-A-S 你为什么在乎?知道这些信息对您有何帮助?
  • "程序怎么知道我们引用的是a引用的对象的类型,还是栈上变量a的类型?"这取决于您在谈论什么代码。例如,虚方法调用将使用运行时对象类型,而方法重载解析将使用编译时类型。或者换一种说法:你实际上并没有正确定义问题。你所说的“变量”是什么意思,你是指被引用的对象,还是做你的意思是对象引用本身?

标签: c# oop


【解决方案1】:

TLDR:是的,当该程序集被引入运行时时,编译时 Animal 类型元数据完整存在。尽量避免在您的应用中使用反射,但如果您确实需要它,请小心行事。

.NET 有一个强大的类型系统来支持动态运行时链接(这就是类库项目类型编译为 DLL 的原因)。如果开发人员表达了与类型系统相反的任何内容,则应在程序集中对其进行捕获。

在编译期间,编译器采取的立场是,它不应该假设其他程序集的类型在运行时如何使用该类型(即使它们被标记为私有 - 请查看 System.Reflection)。

恕我直言,反射是 C#/.NET(和 VB.NET)从经典的 VB 时代和 Java 元数据系统中获得的罪恶乐趣之一……能够在静态类型系统,但是有很多开销和可以/应该尽可能避免的问题。

【讨论】:

    【解决方案2】:

    编译时类型 Animal 是栈上变量 a 的类型吗?

    是的(尽管说on the stack 是不必要的——它是否在堆栈上无关紧要)。

    检查 IL 以获取 SharpLab 的代码:

    using System;
    using System.Reflection;
    
    public class Animal{}
    
    public class Dog: Animal {}
    
    public class Program
    {
        
        public static void Main()
        {
            Animal a = new Dog();
    
            ShowType(a);
            
            MethodInfo mi = typeof(Program).GetMethod("Main");
            MethodBody mb = mi.GetMethodBody();
    
            foreach (LocalVariableInfo lvi in mb.LocalVariables)
            {
                Console.WriteLine("Local variable: {0}", lvi.LocalType);
            }
        }
        
        public static void ShowType<T>(T o)
        {
            Console.WriteLine(typeof(T)); // Animal
            Console.WriteLine(o.GetType()); // Dog
        }
    }
    

    注意,特别是:

    .locals init (
        [0] class Animal a
    )
    

    这清楚地表明变量的类型是Animal(这并不奇怪,它总是与代码一致)。

    还要注意反射(在运行时)输出Animal不是Dog)。很明显,方法的元数据是存储变量类型。

    【讨论】:

    • 我正在尝试考虑运行时需要引用堆栈上的类型的场景。编译器完成后,运行时只需要遵循 IL。那时一切都已完成,类型并不重要。编译器只发出在编译时安全的调用。
    • @Enigmativity 我不是在争论它是否有用。我只是指出它在那里(即Does compile-time type still exist at runtime? 的答案是Yes)。 Local variable: Animal 是输出的内容。我怀疑这样做的原因是因为您可以附加一个调试器并为其分配一个新值 - 因此了解变量类型以明确 any 很有帮助Animal 可以分配给它。
    • 这实际上是OP提出的一个有趣的问题。这让我开始思考。
    • @Enigmativity 我的意思是,这在很大程度上是一个毫无意义的问题(因为答案并不重要)。 :)
    • 是的,确实如此。在我看来,一旦您通过静态类型语言的编译,类型就不再重要了。我想这就是静态类型的意义所在。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-26
    • 1970-01-01
    • 2011-02-22
    • 2020-08-26
    • 2022-01-27
    相关资源
    最近更新 更多