Saeed Neamati
Saeed Neamati

Reputation: 35842

What's the explanation of this behavior in C#

Consider this simple console application:

class Program
{
    static void Main(string[] args)
    {
        var human = CreateHuman(args[0]);
        Console.WriteLine("Created Human");
        Console.ReadLine();
    }

    public static object CreateHuman(string association)
    {
        object human = null;
        if (association == "is-a")
        {
            human = new IsAHuman();
        }
        else
        {
            human = new HasAHuman();
        }
        return human;
    }
}

public class IsAHuman : Human
{
}

public class HasAHuman
{
    public Human Human { get; set; }
}

The Human class is in another assembly, say HumanAssembly.dll. If HumanAssembly.dll exists in the bin directory of our console app, everything would be fine. And as we might expect, by removing it we encounter FileNotFoundException.

I don't understand this part though. Comment human = new IsAHuman(); line, recompile and remove HumanAssembly.dll. Console app won't throw any exception in this case.

My guess is that CLR compiler differentiates between is a and has a associations. In other words, CLR tries to find out and understand and probably load all the types existing in the class definition statement, but it can instantiate a class without knowing what's inside it. But I'm not sure about my interpretation.

I fail to find a good explanation. What is the explanation for this behavior?

Upvotes: 1

Views: 119

Answers (2)

Hans Passant
Hans Passant

Reputation: 941873

You are seeing the behavior of the JIT compiler. Just In Time. A method doesn't get compiled until the last possible moment, just before it is called. Since you removed the need to actually construct a Human object, there is no code path left that forces the jitter to load the assembly. So your program won't crash.

The last remaining reference to Human is the HashAHuman.Human property. You don't use it.

Predicting when the jitter is going to need to load an assembly is not that straight-forward in practice. It gets pretty difficult to reason through when you run the Release build of your code. That normally enables the optimizer that's built into the jitter, one of its core optimization strategies is to inline a method. To do that, it needs access to the method before it is called. You'd need an extra level of indirection, an extra method that has the [MethodImpl(MethodImplOptions.NoInlining)] attribute to stop it from having a peek. That gets to be a bit off into the deep end, always consider a plug-in architecture first, something like MEF.

Upvotes: 4

brainless coder
brainless coder

Reputation: 6430

Here is great explanation of what you are looking for.

The CLR Loader

Specially in the following lines -

This policy of loading types (and assemblies and modules) on demand means that parts of a program that are not used are never brought into memory. It also means that a running application will often see new assemblies and modules loaded over time as the types contained in those files are needed during execution. If this is not the behavior you want, you have two options. One is to simply declare hidden static fields of the types you want to guarantee are loaded when your type is loaded. The other is to interact with the loader explicitly.

As the Bold line says, if you code does not execute a specific line then the types won't be loaded, even if the code is not commented out.

Here is also a similar answer that you might also be interested in -

How are DLLs loaded by the CLR?

Upvotes: 1

Related Questions