Run string as code with CodeDom

I can already guarentee that this is not working due to my own incompitence but I can’t get it to work.

I need to be able to parse a string as code for several reasons (i.e. getting a component with a type that is stored in a string). I’m trying to use code from Sebastian Lague’s video on making a coding game with some modifications to simplify the input required. However, an error occurs at line 11 suggesting that ‘results’ is a null reference.

Problem Method:

public static object RunStringAsCode(string str, object src, params object[] paramaters)
{
    CSharpCodeProvider provider = new();
    string code = @"using UnityEngine;
                    public class MyClass {
                    public object MyMethod(object[] para){" +
                    str +
                    @"}
                    }";
    CompilerResults results = provider.CompileAssemblyFromSource(new CompilerParameters(), code);
    Type classType = results.CompiledAssembly.GetType("MyClass");
    System.Reflection.MethodInfo method = classType.GetMethod("MyMethod");

    return method.Invoke(src, paramaters);
}

Example use case:

RunStringAsCode($"return para[0].GetComponent<{componentType}>();", this, this);

That is not valid C#. You need to put that return in a static method inside a (static) class. Something along the line of this:

using UnityEngine;
public static class MyClass
{
    public static object MyMethod(object[] para)
    {
         return para[0].GetComponent<{componentType}>();
    }
}

Right now you are calling an instance method of a class that you did not instantiate. Better to use static for this. You can also use MonoBehaviour as return type and GameObject[] as input, it needn’t be object.

You should also log the resulting code string to better see any syntax errors.

Generally you may be better off either using a switch on strings rather than compiling code, or if it needs to be code, use a scripting language like Lua-CSharp.

Granted. :stuck_out_tongue_closed_eyes:

1 Like

Thanks for replying.

Shouldn’t the ‘code’ string in the ‘RunStringAsCode’ method compensate for the lack of the instantiated class in the example as this is added in the method?

I have also tried changing ‘MyClass’ and ‘MyMethod’ to static although that hasn’t fixed the issue.

I might just be misinterpreting your answer though :P.

No. Also the src you pass in isn’t the object you got the method for, which is entirely in your string. For static invoke you just pass null, not an instance.

1 Like

Again, thank you, although, not really helpful as passing the exact lines you gave me as the ‘code’ string (with component type switched out for a valid type) did not work.

public static object RunStringAsCode(string str, params object[] paramaters)
{
    CSharpCodeProvider provider = new();
    string code = @"using UnityEngine;
                    public static class MyClass
                    {
                        public static object MyMethod(object[] para)
                        {
                            return para[0].GetComponent<Unity.UI.RawImage>();
                        }
                    }";
    CompilerResults results = provider.CompileAssemblyFromSource(new CompilerParameters(), code);
    Type classType = results.CompiledAssembly.GetType("MyClass");
    System.Reflection.MethodInfo method = classType.GetMethod("MyMethod");

    return method.Invoke(null, paramaters);
}

What’s the output (error) you get when you run it?

You know you can debug your code to see what it does the moment it does it?

It’s a null reference exception. Specifically it seems that ‘results’ is null and the exception occurs when it is called in Type classType = results.CompiledAssembly.GetType("MyClass");

I don’t understand what you are trying to achieve with your code. Are you trying to create C# code dynamically, that uses the Unity API while using reflection to invoke it?

Why don’t you try untying the guardian knot blindfolded with your teeth? That would be less complicated.

Any way, you seem to lack basic understanding of C# and generally statically strongly typed languages, the null reference is the least of your problems. Here are some problems with you code:

  • You are using GetComponent on an instance of type object, why do you think this would even compile? objects don’t have a GetComponent method.
  • You are trying to invoke a method in a type that doesn’t have that method. src type won’t neccesarily have the ‘MyMethod’
  • You are passing a parameters object to a parameters array
  • You have not instantiated the instance of the type that has the method
  • What the method returns has to be cast back to the type you need
  • You cannot use the code in the string without referencing the required assemblies that contain the definitions of the types you use

All that plus more, will make this code even if you managed to make it work, extremely slow as it creates a class, writes it to disk, compiles it, uses reflection and then deletes it.

Also would make it platform specific, os specific and compilation specific as the required assemblies would have to be binaries for the specific hardware,os and so on.

Also would make it very hard to debug, very hard to test plus would never be generic enough to run Unity specific commands.

I would suggest that you should try to learn some basic C# stuff first, like what is a strongly typed language, the differences between instances and types before trying something like this. Right now it is way out of your current level.

Anyway, this is the code that works with what (I think) you try to do:

   public static object RunStringAsCode(string str, object src, params object[] parameters)
   {
      CSharpCodeProvider provider = new();
      StringBuilder code = new StringBuilder();
      code.Append("      using UnityEngine;                         \r\n");
      code.Append("      using System;                              \r\n");
      code.Append("      public class MyClass                       \r\n");
      code.Append("      {                                          \r\n");
      code.Append("        public object MyMethod(GameObject param) \r\n");
      code.Append("        {                                        \r\n");
      code.Append(            str                                        );
      code.Append("        }                                        \r\n");
      code.Append("      }                                          \r\n");

      CompilerParameters compParams = new CompilerParameters();
      
      compParams.ReferencedAssemblies.Add(@"C:\Program Files\Unity\Hub\Editor\6000.0.35f1\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.dll");
      compParams.ReferencedAssemblies.Add(@"C:\Program Files\Unity\Hub\Editor\6000.0.35f1\Editor\Data\MonoBleedingEdge\lib\mono\4.8-api\Facades\netstandard.dll");
      
      CompilerResults results = provider.CompileAssemblyFromSource(compParams, code.ToString());
      Type classType = results.CompiledAssembly.GetType("MyClass");
      System.Reflection.MethodInfo method = classType.GetMethod("MyMethod");
      
      var instance = Activator.CreateInstance(classType);
      
      return method?.Invoke(instance, parameters);
   }

And it runs from a Monobehaviour like this:

public class BehaviorReflection : MonoBehaviour
{
    private readonly Type componentType = typeof(SpriteRenderer);
    private SpriteRenderer sr;
    
    private void Start()
    {
        var result = TestReflection.RunStringAsCode($"return (object)(param.GetComponent<{componentType}>());", this, gameObject);
        
        sr = result as SpriteRenderer;
        if(sr != null)
            sr.color = Color.red;
    }
}
1 Like

I can’t say I’m particularly thankful for the collection of letters you spat on and forced into my retinas that seems to insult my very method without the slightest consideration for what I actually said.

To be clear, I took the code directly from the start of a Sebastion Lague video with a slight modification with expectation of it working as it did in the video and then attempted to solve the issue despite the incredible lack of information I had to work with in both the Unity Console and Debugging mode.

I’m not new to C# nor do I have no knowledge of how it works. However, I am also not a senior developer and expected code that worked for someone else to work for me. While I appreciate that you raised some valid points and attempted to solve my issue, what you gave me did not work (providing the exact same error) and it came accompanied by an insult to my abilities to program in the first place.

I know I’m overreacting, but I’d still appreciate a solution to the example I used assuming I can be given some respect as an anonymous individual. The problem is as follows:

  • I have a string that holds the path for a monobehaviour type (e.g. “UnityEngine.UI.RawImage”)
  • I need to access the component attatched to the current gameObject using said type
  • Therefore, I searched for a reasonable method to run the entire line of code from a string so the type could be inserted.

I just so happened to end up down this rabbit hole and stayed with it hoping it would be useful for other problems in the future.

If truth insults you then you should try to actually become better coder instead of just being insulted, nothing of what I said about why your code doesn’t work is false.

This is the worst possible way for someone to code, copying code from someone else without actually understanding how or why it works and making “slight” modification.

You modification was not “slight”. You actually try to call Unity’s API from generated code and you try to use Unity’s types like GameObjects and Components, completely different from what happens in the video which is basic arithmetic calculations.

Then you should know basic things you have written like object.GetComponent, like the differences between types and instances of that type and not try to call methods from the wrong types.

The only reason the error is the same is because the compilation fails and there is no result to be returned. The code I wrote works, unless you copied it without understanding what it does.

Did you even check if the required dlls are in the same folder on your machine? Did you change the paths to the correct dlls for your own platform?

I’ve given you the solution that works.

Why would you think I disrespect you as an individual? Because I point out your mistakes? Because I say that code like this is way above your level when obviously it is, considering your lack of knowledge of the things I mentioned before? Because I suggested that you should learn some basic C# stuff instead of just copying code from videos and expecting to work?

If you think that copying code from a video and making not so slight modifications -while expecting to work- because it works for someone else is normal, it isn’t and I’m not trying to disrespect you here, it’s the truth.

You should try to read and understand why your code is not working, for example by googling CompilerResults C# the first result is https://learn.microsoft.com/en-us/dotnet/api/system.codedom.compiler.compilerresults?view=windowsdesktop-9.0. This contains the code of DisplayCompilerResults that if you change all the Console.WriteLine with Debug.Log will tell you exactly what is happening with the compilation of your runtime generated class. It will show the errors and you can see why it is returning null.

1 Like

Then just Type.GetType and then use the overload of GetComponent that takes a System.Type instance.

Not sure how you ended up down this mad rabbit hole.

1 Like