-1

Let Dog be a class inherited from Animal class, we can do the instantiation as follows:

Animal a = new Dog();

I know that at compile time, the compiler only knows that we are declaring a as an Animal, and when it's at runtime, we are pointing a to a Dog object. But there is something vague to me at this point:

Is the compile-time type Animal being the type of the variable a on the stack? Or is it just an information telling the compiler at compile time what type of object may be referenced by a and has nothing to do with the type of the variable a on the stack?


I am asking this because I'm wondering if the information on the compile-time type Animal still exists at runtime or if it will become completely irrelevant and is discarded? If it still exists, is it stored as the type of the variable a on the stack? And if so, when we are referring to the type of a, how does the program know if we are referring to the type of the object referenced by a or the type of the variable a on the stack?

I'm new to this concept and may have some misunderstandings. Could anyone please clarify for me? Thanks in advance!

J-A-S
  • 368
  • 1
  • 8
  • 2
    You can try by `a.GetType()` to check runtime type of the object – Fabio Aug 01 '21 at 01:18
  • For local variables, I'm not sure. But for fields, the compile time type is definitely retained at runtime. You can get it with reflection. – Sweeper Aug 01 '21 at 01:21
  • @Fabio Thank you for your comment :) so may I ask if the variable ```a``` on the stack (not the object it is referencing to on the heap) is of type ```Dog``` at runtime? – J-A-S Aug 01 '21 at 01:25
  • Looks like I was wrong. Apparently, the [static type](https://stackoverflow.com/a/11634832/8967612) does get discarded if you compile in Release config. See [this example](https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQMwAJboMLoN7LpGYZQAs6AsgBQCU+hxTAggHYCWAtgIYA263dAF50rAKYB3dABEA9gHM6AbkZMiUOAAZ0AZ2ECAdABVZAZQAuAJ3atFtJUQD0j9CfQAzWZYDGY9MDFvXm5LbnN2WVZ0G3QAJTFeMW4dMQNVNQ0ATmoAcTFzCzD2byMATwAHMWpuWgMAOW5OMXt0gF90jQA2Vwq/PILzIpLegB4jAD5qI3QAD3o8TAB2dHNe2Xcp+3R2pB3kLDYuPnxt/ZgZBXQQdEOefgWdoA=) and switch between Debug and Release. – 41686d6564 stands w. Palestine Aug 01 '21 at 01:36
  • 1
    @J-A-S Why do you care? How will knowing that information help you in any way? – mjwills Aug 01 '21 at 02:01
  • `And if so, when we are referring to the type of a, how does the program know if we are referring to the type of the object referenced by a or the type of the variable a on the stack?` Who / what is `we` in this context? – mjwills Aug 01 '21 at 02:43
  • 1
    @41686d6564 No, that example discards the whole variable, because it served no purpose. If the variable was there it would retain `Animal` as the type – Charlieface Aug 01 '21 at 02:46
  • @41686d6564 Thank you for your link :) I'm still a bit confused on what type the variable ```a``` (not its referent) is before and after compilation. Is the static type stored as the type of the variable ```a```? – J-A-S Aug 01 '21 at 02:47
  • @Sweeper - I think that's confusing. The compile-time types are not retained at run-time. The compiler determines what operations can be run on the instance based on it's compile-time type, but at run-time the type is always what it was instantiated as. – Enigmativity Aug 01 '21 at 02:48
  • 1
    "how does the program know if we are referring to the type of the object referenced by `a` or the type of the variable `a` on the stack?" It depends on what code you are talking about. For example, a virtual method call will use the at-run-time object type, whereas method overload resolution will use the compile-time type. Or putting it another way: **you haven't actually defined the question peroperly.** What do you mean by *"the variable"*, do you mean the object being referenced, or do you mean the object reference itself? – Charlieface Aug 01 '21 at 02:50
  • @Charlieface for the variable I mean the one that is pointing to the actual object (the one that holds the reference of the object) – J-A-S Aug 01 '21 at 03:07
  • The reference can only be used in the way it was declared at compile time. But it still points to an object which may be a more derived type. The code that can be written is defined at compile time, so I'm not sure how the runtime type is relevant. Still not sure what you're really asking, why do you care if the runtime "knows" what you have, all that you care is that the code you write will be executed – Charlieface Aug 01 '21 at 03:10

2 Answers2

4

Is the compile-time type Animal being the type of the variable a on the stack?

Yes (although saying on the stack is unnecessary - whether it is on the stack or not is irrelevant).

Check the IL for the code at 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
    }
}

Note, in particular:

.locals init (
    [0] class Animal a
)

which clearly shows that the type of the variable is Animal (which is no surprise, it will always be consistent with the code).

Note also that reflection (at runtime) outputs Animal (not Dog). So clearly the metadata of the method is storing the variable type.

mjwills
  • 23,389
  • 6
  • 40
  • 63
  • 1
    I'm trying to think what scenario the run-time needs to refer to the type on the stack. Once the compiler has done it's thing the run-time just needs to follow the IL. Everything is baked in at that point and types aren't important. The compiler only issues calls that were safe at the time of compiling. – Enigmativity Aug 01 '21 at 03:01
  • 1
    @Enigmativity I am not arguing whether it is useful. I am just pointing out that it is _there_ (i.e. the answer to `Does compile-time type still exist at runtime?` is `Yes`). `Local variable: Animal` is what is outputted. I _suspect_ the reason it is done is because you could attach a debugger and assign a new value to it - so it is helpful to know the variable type to be clear that _any_ `Animal` could be assigned to it. – mjwills Aug 01 '21 at 03:02
  • It's actually an interesting question that the OP has put forward. It's got me thinking. – Enigmativity Aug 01 '21 at 03:04
  • @Enigmativity I mean, it is largely a _pointless_ question (since the answer doesn't matter). :) – mjwills Aug 01 '21 at 03:05
  • 1
    Yes, that's very true. It seems to me that types don't matter much at all once you get past compilation in a statically-typed language. I guess that's the point of being statically-typed. – Enigmativity Aug 01 '21 at 03:07
-1

TLDR: yes, the compile-time Animal type metadata exists in it's entirety when ever that assembly is brought in to the runtime. Try to avoid using Reflection in your app if possible, but if you truly need it, tread lightly.

.NET has a robust type system to support dynamic run-time linking (which is why class library project types compile down to DLLs). If a developer expresses anything against the type system, it should be captured within the Assembly.

During compilation, the compiler takes the stance that it shouldn't assume how that type could be used by other Assemblies' types are used during runtime (even if they are marked as private - check out System.Reflection).

IMHO, Reflection is one of those guilty pleasures that C#/.NET (and VB.NET) took from classic VB days and Java metadata systems... there's a lot of power in being able to "dynamically" work within a static type system, but there's lots of overhead and issues that can/should be avoided it at all possible.

isandburn
  • 134
  • 5