[Closed] C#: compiled classes disappearing?
It seems like any time a C# assembly is compiled, previously loaded assemblies are cleared from memory.
For example, if I run this:
(
classStr1 = "class class1{}"
classStr2 = "class class2{}"
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.GenerateInMemory = true
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerResults = (csharpProvider.CompileAssemblyFromSource compilerParams #(classStr1))
compilerResults = (csharpProvider.CompileAssemblyFromSource compilerParams #(classStr2)) --this compilation causes the previous one above to be cleared from memory
)
I get the following results:
dotnetclass "class1"
undefined --returned result
dotnetclass "class2"
dotNetClass:class2 --returned result
So it seems like every time CompileAssemblyFromSource is called, previously compiled assemblies are cleared from memory. I even tried setting my compiler params to generate to a file and then manually loading that file with dotnet.LoadAssembly…and still, each compilation would clear out previously loaded assemblies.
How do I get max to retain previously compiled assemblies in memory? Simply compiling multiple assemblies together at the same time won’t work because this is a problem that’s occurring when I try to compile multiple independent scripts after each other. Calling setLifetimeControl #dotnet on my classes also has no effect.
I would suspect that what is failing is not the assembly in memory, but the name resolution of the dotNetClass mechanism.
If it were that only one assembly can ever be in memory, many many things would fail.
If you save each compiled assembly to a variaible and use assembly.createInstance “Class1”/“Class2” respectively it should work.
Thanks lo,
The [assembly].CreateInstance method suffers the same problem as default maxscript constructor (returns undefined for any previously compiled classes).
However, the C# Activator class has a CreateInstance method that works when you pass a Type to it rather than a string, and I can get the type of the previously compiled class by looping through all loaded assemblies and finding the one I need by name. Kind of a pain but at least it works!
Can it be useful to save the dotnet classes to global variables and then use them directly?
(
classStr1 = "class class1{}"
classStr2 = "class class2{}"
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.GenerateInMemory = true
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerResults = (csharpProvider.CompileAssemblyFromSource compilerParams #(classStr1))
[b]global classStr1_Compiled = dotnetclass "class1"[/b]
compilerResults = (csharpProvider.CompileAssemblyFromSource compilerParams #(classStr2)) --this compilation causes the previous one above to be cleared from memory
[b]global classStr2_Compiled = dotnetclass "class2"[/b]
)
It’s a really anoying problem that I had previously never found (luckily).
And as @lo has said, you can save each compiled assembly to a variable and use assembly.createInstance to create dotnet objects:
(
fn compileCSharpCode classStr =
(
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
dotnet.setlifetimecontrol compilerParams #dotnet
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.ReferencedAssemblies.Add("System.Core.dll");
compilerParams.GenerateInMemory = on
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(classStr)
dotnet.setlifetimecontrol compilerResults #dotnet
compilerResults.CompiledAssembly*
)
classStr1 = "class class1{}"
classStr2 = "class class2{}"
global classStr1_Compiled = compileCSharpCode classStr1
global classStr2_Compiled = compileCSharpCode classStr2
)
And then:
Class1Object =*classStr1_Compiled.CreateInstance “class1”
Class2Object =*classStr2_Compiled.CreateInstance “class2”
Of course with the first method shown in post #4 you can create dotnet objects through:
Class1Object = dotnetObject classStr1_Compiled
Class2Object = dotnetObject classStr2_Compiled
Thanks aaandres,
It seems like your method of saving the classes to a variable immediately after they’re compiled is also working. Trying to generate an instance from the class string will fail but your method of using the cached class variable succeeds.
i think it’s a part of how max’s dotnet works. max creates a hash table with names of classes to be able create them by name. so youevery timeoverride the value defining a class with the same name (key)
assemblies stay unique of course. and you right, you can use activator instead of max create instance mechanism.*