[PURPOSE]
I need to use reflections to
- Load an assembly into a separate ApplicationLoadContext
- Create a type from it
- Create an instance from the type
- Invoke functions on the instance repeatedly
- After some time, perhaps days, when this assembly is no longer needed, unload the ApplicationLoadContext then delete the assembly file.
If you want to know why I am doing this, please see skybridge.net.au.
[The problem]
Although I have set all references to the assembly, type and instances to null before unloading the ApplicationLoadContext, it still failed to unload.
[Code demo 1]
The following code shows how my code works. It failed to delete the DLL file:
namespace MyDll
{
    public class MyText
    {
        public string m_text;
        public MyText(string text)
        {
            m_text = text;
        }
        public string GetText()
        {
            return m_text;
        }
    }
    public class MyClass
    {
        public string GetTextByBytes(byte[] bytes)
        {
            string json = Encoding.UTF8.GetString(bytes);
            var obj = JsonConvert.DeserializeObject<MyText>(json);
            return obj.GetText();
        }
        public string GetTextByObject(MyText text)
        {
            return text.GetText();
        }
        public string GetGuidText(Guid guid)
        {
            return guid.ToString();
        }
        public string GetText(string str)
        {
            return str;
        }
        public List<string> GetMultipleTexts(List<string> lst)
        {
            return lst;
        }
    }
}
    static void Main(string[] args)
    {
        var ctx = new AssemblyLoadContext(null, isCollectible: true);
        var assembly = ctx.LoadFromAssemblyPath(dllFilePath);
        var myType = assembly.GetType("MyDll.MyClass");
        var instance = Activator.CreateInstance(myType, new object[] { }, null);
        var myMethod = myType.GetMethod("GetGuidText");
        var res = myMethod.Invoke(instance, new object[] { Guid.NewGuid() });
        assembly = null;
        myType = null;
        instance = null;
        myMethod = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Thread.Sleep(2000);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        ctx.Unload();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Thread.Sleep(2000);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        File.Delete(dllFilePath);          
        Console.WriteLine($"Done!");
    }
[Code demo 2]
But, if I put the code in a separate function, Test(), the deletion works:
    private static void Test(AssemblyLoadContext ctx)
    {
        var assembly = ctx.LoadFromAssemblyPath(dllFilePath);
        var myType = assembly.GetType("MyDll.MyClass");
        var instance = Activator.CreateInstance(myType, new object[] { }, null);
        var myMethod = myType.GetMethod("GetGuidText");
        var res = myMethod.Invoke(instance, new object[] { Guid.NewGuid() });
    }
    static void Main(string[] args)
    {
        var ctx = new AssemblyLoadContext(null, isCollectible: true);
        Test(ctx);
        ctx.Unload();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Thread.Sleep(2000);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        File.Delete(dllFilePath);         
        Console.WriteLine($"Done!");
    }
[But I can't change my code to the demo 2 style]
In my SkyBridge app, the loaded assembly and the types and instances loaded from this assembly are placed into a global collection, to be invoked by a separate thread. So I can't put them into a function as local variables and let them go out of scope when that function exits, like in [Code demo 2].
Instead, when an assembly is no longer needed, all I can do is like [Code demo 1], to remove them from the global collection, set their references to null, and hope GC can collect them so I can unload and delete. But it always fails.
[Final Question]
Is there anything that I can do in [Code demo 1] to enable the unloading and deletion, without putting the types and instances into a function as local variables?
********* Added on 2023-06-02 **********
Now the problem becomes clearer. If a local variable is declared in a function, when that function ends, that variable goes out of scope. When I call GC.Collect, it is immediately collected. However, if this variable is a global variable that never goes out of scope, even if I set it to null then call GC.Collect, it is not collected.
Is there a way to force a global variable that never goes out of scope to be immediately collected after GC.Collect?