Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do you set a .NET enum from JavaScript? #156

Closed
JohnLudlow opened this issue Jan 21, 2020 · 10 comments
Closed

How do you set a .NET enum from JavaScript? #156

JohnLudlow opened this issue Jan 21, 2020 · 10 comments
Assignees
Labels

Comments

@JohnLudlow
Copy link

How can I set a .NET enum from JavaScript. The common use case is an object with a property of an enum type or method which takes an enum, but here is a simplified (slightly contrived) use case: setting an enum variable.

enum Status
{
     Succeeded,
     Failed,
     InProgress,
 }

using (var host = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging, 9002))
{
   host.AddHostType(typeof (Status));
   host.Script.status = Status.Succeeded;
   var a = (host.Evaluate("status"));
   var b = (host.Evaluate("status = Status.Failed"));
}

In this case, b and host.Script.status are undefined. How can we create the correct value from the enum here?

@ClearScriptLib
Copy link
Collaborator

Hi @JohnLudlow,

The issue here is that Status isn't a public type. ClearScript strictly enforces member visibility even for explicitly exposed types.

There are several ways to make the Status fields visible to script code. For example, you could make Status a public type. Another possibility is to expose it with the private access option:

host.AddHostType(HostItemFlags.PrivateAccess, typeof(Status));

See ScriptEngine.AccessContext for another alternative.

Good luck!

@JohnLudlow
Copy link
Author

Dang how did I miss that?

Thanks

@ClearScriptLib
Copy link
Collaborator

Please reopen this issue if you have additional questions or concerns about this topic. Thank you!

@JohnLudlow
Copy link
Author

Will do, but I think that answers it in terms of enums.

Thanks

@Brain2000
Copy link

I noticed that I wasn't able to find a function to set an enum from a plain number being passed in, which surprised me. Byte, int32, int64, nullable, string, ScriptObject... so much just works! Since an enum is backed by an int32 by default, I thought it would have been simple to use just a number.

@ClearScriptLib
Copy link
Collaborator

Hi @Brain2000,

Since an enum is backed by an int32 by default, I thought it would have been simple to use just a number.

The behavior here is defined by C#, which doesn't implicitly convert integers to enums. Consider this simple example:

public enum Kingdom { Plant, Animal };
public class Being { public Kingdom Kingdom; }

And then:

engine.AddHostType(typeof(Kingdom));
engine.Script.me = new Being();

To assign an integer value to me.Kingdom, you have to perform an explicit cast, just like in C#. ClearScript provides HostFunctions.cast for this purpose. To summarize:

engine.Script.host = new HostFunctions();
engine.Execute(@"
    me.Kingdom = Kingdom.Animal;         // OK
    me.Kingdom = host.cast(Kingdom, 1);  // OK; explicit conversion
    me.Kingdom = 0;                      // OK; SPECIAL CASE: zero is implicitly convertible to all enums
    me.Kingdom = 1;                      // ERROR
");

Good luck!

@EtienneLaneville
Copy link

EtienneLaneville commented Jun 16, 2022

Is there a way to get the integer value of an enum on the script side? I can get the name using .ToString but I am looking for something like .ToInt32. I tried using HostFunctions's toInt32 method but I get a 'Type mismatch' exception. Since the enum is converted to an object, this makes sense but I am hoping there is a way to somehow get the integer value.

Ideally the Is keyword would be supported in VBScript Case statements (Case Is Kingdom.Plant for example) and there would be no need to convert the enum to string or integer.

@ClearScriptLib
Copy link
Collaborator

Hello @EtienneLaneville,

Is there a way to get the integer value of an enum on the script side?

Sure. You can use HostFunctions.cast:

engine.AddHostObject("host", new HostFunctions());
engine.AddHostType(typeof(Int32));
engine.Execute("MsgBox host.cast(Int32, Kingdom.Animal)");

Another possibility is expose a managed function that performs the conversion:

engine.Script.EnumToInt = new Func<Enum, int>(Convert.ToInt32);
engine.Execute("MsgBox EnumToInt(Kingdom.Animal)");

I tried using HostFunctions's toInt32 method but I get a 'Type mismatch' exception.

Right. Those functions are designed for a different purpose – namely, to allow script code to specify the exact numeric type of a host method argument in order to avoid ambiguity in specific scenarios.

Ideally the Is operator would be supported in VBScript Case statements

Unfortunately, VBScript's Select Case statement only works with numbers and strings. However, it should work in conjunction with one of the suggestions above.

Cheers!

@EtienneLaneville
Copy link

EnumToInt function works great and you don't even need to add the enum as a host type if you are just converting an object's property:

engine.Execute("MsgBox EnumToInt(me.Kingdom)");

Instead of using engine.Script.EnumToInt = new Func<Enum, int>(Convert.ToInt32);, is there a way to add a host function like one would add an object? If I have a function delegate, is there something like engine.AddFunction("EnumToInt", delegate);. I couldn't find anything like this in the API documentation.

@ClearScriptLib
Copy link
Collaborator

Hi @EtienneLaneville,

Instead of using engine.Script.EnumToInt = new Func<Enum, int>(Convert.ToInt32);, is there a way to add a host function like one would add an object?

A delegate is an object, so AddHostObject is the right call:

engine.AddHostType(typeof(Kingdom));
engine.AddHostType(typeof(Console));
engine.AddHostObject("EnumToInt", new Func<Enum, int>(Convert.ToInt32));
engine.Execute("Console.WriteLine(EnumToInt(Kingdom.Animal))");

The following all do pretty much the same thing:

engine.AddHostObject("foo", obj);
engine.Script.foo = obj;
engine.Script["foo"] = obj;

AddHostObject is special in that it creates immutable items and supports HostItemFlags.

Good luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants