Adding members to C# objects

Developer
Apr 5, 2012 at 12:16 AM

Yair Benyami sent this message to me privately, but I think others might be interested.

 

Hi Oliver !

...

The current version prohibits adding new methods using javascript to an object set using SetParameter().
For example: I use 
    Context.SetParameter("obj" new Class1())
and then try to add:
    Context.Run("obj.newfunc = function() {..}");

This yells out an "unknown member" exception.

Now, you could ask why would I need to add new function from JS ??
The answer is that I need my method to include a callback function, so that the caller can use the following JS:
    obj.doCallback('prm1', 'prm2', function() { ... } );

But passing a JS function back to .net isn't supported, so I'm adding a helper method using javascript:
    Context.Run('obj.doCallbackReal = function(prm1, prm2, func) { obj.doCallback(prm1,prm2, func.toString()) }')
and simply hide the underlying doCallback by prefixing it with an underscore.
I appreciate your time, hope you have some thoughts about this..
Best Regards,
Yair.
Developer
Apr 5, 2012 at 12:29 AM

I added the error checking that is being triggered for my own benefit, but I would like to find a way to make it work for everybody.  I use javascriptdotnet as a scripting language, and I expose objects from the main application to the scripts.  In this situation if a script writer assigns to an unknown property then he has made a mistake (e.g. he has misspelled a property name, or has wrong capitalization).  In this use case the error is helpful.

However I can see that what Yair wants is also reasonable and fits the JavaScript ethos.  I suggest a new memberless C# interface IFailForUnknownProperties.  When a C# object implements this IFailForUnknownProperties then the (new) error handling will work as it does in 0.6.  If it is not implemented then we revert to the behaviour in 0.4.

Thoughts?

Apr 5, 2012 at 1:31 AM
Edited Apr 5, 2012 at 1:34 AM

Good idea, it would be great to have control over this kind of behavior for specific types.

How about using a .NET attribute to place on the type we want to alter the behavior?

something like:

 

public enum UnknownPropertyHandlingMode
{
    Fail,
    Append,
};
public class UnknownPropertyHandlingAttribute: System.Attribute
{
    public UnknownPropertyHandlingMode Mode
    {
        get;
        set;
    }
};

[UnknownPropertyHandling(Mode = UnknownPropertyHandlingMode.Fail)]
class MyClass
{
[...]
};

The names I chose for the attribute or the enumeration might not be the best. It's just so you get the idea.

Maybe we could even push further with a more generic .NET attribute name, like [JavascriptDotNetTypeAttribute(UnknownPropertyHandling = UnknownPropertyHandling.Fail)], so then the attribute will be extensible and more appropriately named if we shall add some new behavior alterations in the future. This will avoid having to create a new attribute for each behavior we want to make alterable on a specific type.

Make sense?

Apr 5, 2012 at 1:04 PM

The attribute approach is probably better aligned with the usual .NET best practices.
I guess I would recommend it against the interface approach.

 

- Mike
Member of the Noesis Team

Apr 5, 2012 at 2:09 PM

How about adding an optional parameter to .SetParameter() method ?

context.SetParameter('main', new MainJsObject(), true|false);

This way you are less likely to forget about it. I define many classes which use .SetParameter() and I need to allow all of them to allow adding members dynamically.

It's much easier for me to call my own SetParameter in my own object wrapper for Noesis Javascript Context. 

 

One more thing, 

In the near future we are going to build an extension mechanism to allow our customers to build their own API.

I want them to pass my API a plain object, WITHOUT extra dependency of such attribute/interface.

 

Yair.

Apr 5, 2012 at 2:27 PM

Another option is to add a method overload on .SetParemter

SetParameter(SetParameterArgs obj);

 

class SetParameterArgs{

public string Name {get; set;}

  public object Object {get; set;}

  public bool AllowUnknownProperties {get; set;}

}

 

This way it doesn't "mess" up SetParameter() and allows future settings to be added to SetParameterArgs

Apr 6, 2012 at 4:14 PM

Let's use an enum:

[FlagsAttribute]
public enum SetParameterOptions
{
    None = 0,
    AllowUnknownProperties = 1
}

and overload the SetParameter method with an optional argument.

Developer
Apr 10, 2012 at 6:14 AM

I can see people's objection to interfaces or attributes (although I can imagine situations where they would be useful).  Also, kaniga's suggestion breaks existing code, and makes life a bit complicated for the casual user.  Therefore I have gone with with mboise's suggestion, but reversing the usage so that the old (v0.4 and earlier) behaviour is the default behaviour (ie. when SetParameterOptions is None):

[FlagsAttribute]
public enum SetParameterOptions
{
    None = 0,
    RejectUnknownProperties = 1
}

Yair: does this fix your problem?

Apr 10, 2012 at 7:58 AM

Yes, The enum overload would work just fine.

Thanks for the effort.

 

Yair (=Kaniga)

Apr 11, 2012 at 2:13 AM
Edited Apr 11, 2012 at 2:14 AM
 It works like that:
Context.SetParameter("obj" new Class1())
Context.Run("obj = function(){var _obj = obj; return {method: _obj.method,...};}();");
Context.Run("obj.newfunc = function() {..}");
 
Developer
Apr 11, 2012 at 2:30 AM

Hi krwq,

I'm not sure whether you are asking a question or making a statement.  Are you saying you have tried the new code from subversion?  You code sample does not test the situation discussed because you overwrite 'obj' with an anonymous function before setting the attribute.

Apr 11, 2012 at 11:36 AM
Edited Apr 11, 2012 at 11:38 AM

It is workaround for the problem. This anonyomous function is also called and returns wrapped Class1. This and http://javascriptdotnet.codeplex.com/workitem/8952 is the way I implemented functions like setTimeout and setInterval and they are working pretty nice :) Is there any easier way to implement them?

Developer
Apr 11, 2012 at 11:05 PM
krwq wrote:

It is workaround for the problem. This anonyomous function is also called and returns wrapped Class1. This and http://javascriptdotnet.codeplex.com/workitem/8952 is the way I implemented functions like setTimeout and setInterval and they are working pretty nice :) Is there any easier way to implement them?

Your JavaScript is too clever for me, but I cannot think of another way to do it.  However I should have been more explicit earlier: I have already implemented and committed the change to SetParameter() that I described previously.  Therefore you no longer need your workaround.