Adding getters to .NET types #398
-
I'd like to do
Sadly this does not work. What's the right way to add "extensions" to types that are then available to the proxied instances?? (Ideally it would also be |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
Ok, seems this works:
Note that I'm adding the type as a object rather than as a type. However, this does not seem to work:
If I make |
Beta Was this translation helpful? Give feedback.
-
The .NET and JavaScript type systems are radically different. Interception allows ClearScript to create JavaScript proxies to .NET resources, but these proxies don't follow JavaScript's prototypal inheritance conventions, which simply don't map well to .NET's type system. However, if you're in control of the objects and types you're exposing, you can use .NET's flexibility to emulate aspects of JavaScript's model. Conceptually, the prototype is similar to a static property, so the key is to define such a property in your classes and allow instances to dynamically "inherit" properties from the prototype. One complication here is that any script object you hang on the prototype is specific to its parent script engine – as is the prototype itself if it's also a script object – so each class needs to manage its prototypes in a collection of some sort. Here's a sample that demonstrates this approach. Consider the following: public abstract class JavaScriptLikeObject : DynamicHostObject {
protected IEnumerable<string> GetMemberNames(ClassData classData, ScriptObject obj) {
for (; obj != null; obj = classData.ObjectCtor.InvokeMethod("getPrototypeOf", obj) as ScriptObject)
foreach (var name in (IList)classData.ObjectCtor.InvokeMethod("getOwnPropertyNames", obj))
yield return Convert.ToString(name);
}
protected bool TryGetMember(ScriptObject obj, GetMemberBinder binder, out object result) {
if (obj.InvokeMethod("__lookupGetter__", binder.Name) is ScriptObject getter) {
result = getter.InvokeMethod("call", this);
return true;
}
return obj.TryGetMember(binder, out result);
}
protected class ClassData {
public ScriptObject ObjectCtor;
public ScriptObject Prototype;
public ClassData() {
ObjectCtor = (ScriptObject)ScriptEngine.Current.Global.GetProperty("Object");
Prototype = (ScriptObject)ObjectCtor.Invoke(true);
}
}
}
public abstract class JavaScriptLikeObject<TDerived> : JavaScriptLikeObject {
private static readonly ConditionalWeakTable<ScriptEngine, ClassData> classData = new ConditionalWeakTable<ScriptEngine, ClassData>();
public static ScriptObject prototype => GetClassData().Prototype;
public override IEnumerable<string> GetDynamicMemberNames() => GetMemberNames(GetClassData(), prototype);
public override bool TryGetMember(GetMemberBinder binder, out object result) => TryGetMember(prototype, binder, out result);
private static ClassData GetClassData() => classData.GetOrCreateValue(ScriptEngine.Current);
} Note that public sealed class Unit : JavaScriptLikeObject<Unit> {
public void hello() => Console.WriteLine("Hello from Unit!");
public override string ToString() => "I'm a Unit!";
// etc.
} And now you can do things like this: engine.AddHostType(typeof(Console));
engine.AddHostType(typeof(Unit));
engine.AddHostObject("test", new Unit());
engine.Execute(@"
test.hello();
Unit.prototype.foo = 123;
Console.WriteLine(test.foo);
Unit.prototype.__defineGetter__('boo', function () { return this.ToString(); });
Console.WriteLine(test.boo);
Object.defineProperty(Unit.prototype, 'zoo', { get: function() { return this.GetHashCode(); } });
Console.WriteLine(test.zoo);
Console.WriteLine(new Unit().zoo);
"); This is, of course, just a starting point, but hopefully it gives you an idea of what's possible. Good luck! |
Beta Was this translation helpful? Give feedback.
Hi @AudriusButkevicius,
The .NET and JavaScript type systems are radically different. Interception allows ClearScript to create JavaScript proxies to .NET resources, but these proxies don't follow JavaScript's prototypal inheritance conventions, which simply don't map well to .NET's type system.
However, if you're in control of the objects and types you're exposing, you can use .NET's flexibility to emulate aspects of JavaScript's model.
Conceptually, the prototype is similar to a static property, so the key is to define such a property in your classes and allow instances to dynamically "inherit" properties from the prototype.
One complication here is that any script object you hang on th…