Start a new topic

Use Function with unknown number of arguments

in native javascript you can:


 

var myFunc = function() {
  console.log(arguments);
}

 

Any arguments end up in the arguments object from javascript so can call with what ever. I need to do the same thing in DuoCode...


I have to assign a Delegate or Func or JsFunction or something and i don't no the arguments... can't do a jsobject.test = Func<...> or anything like that... i tried to make a delegate(params dynamic[] args) and assign that to javascript, but only gets the first argument... I end up having to use eval to make dynamically create a native javascript function that simply passes back to c# code the function arguments, plus an additional field i need (that why eval and not Js.referenceAs)


so looks like this:


 

string functionCode = @"[function() {
								return this.OnInternalCallMethod(""[*METHODNAME*]"", arguments);
							}]";
							functionCode = functionCode.Replace("[*METHODNAME*]", att.MethodName);
							dynamic functionDelegate = Global.window.eval(functionCode);
							dynamic_service[functionName] = functionDelegate[0];

 

But there has got to be a better way to do this from C# DuoCode... Please Help... Anybody :)




Note: dynamic_service is just a reference to a object with the this[indexer] so i can throw what ever i want to on that object... I am trying to add a Function (or delegate) at runtime... But i can't use Action or Func and i need to pass and read unknown number of arguments


1) Use __arglist C# keyword to pass, use Js.arguments to access them inside the function, like this

 

[Test]
public void ArgListArguments()
{
  MethodWithArgList(1, __arglist());
  MethodWithArgList(2, __arglist(1));
  MethodWithArgList(7, __arglist(1, 2, 3, 4, 5, 6));
}

private static void MethodWithArgList(int count, __arglist)
{
  QUnit.AreEqual(Js.arguments.length, count);
}

 

So how would i use as a delegate function???


obj.myFunc = delegate(__arglist) {

  var len = Js.arguments.legth;

}


or something like that?

Dude... The Js.argument is the answer itself... I DID NOT KNOW... that no matter what type Func<T1, ... TResult> you use... you can get the ACTUAL params passed to function EVEN IF NOT THE RIGHT ONES SPECIFIED with Func<>...


So i can use Action(()=>{ console.log(Js.arguments); }) OR Func<dynamic>(()=>{ console.log(Js.arguments); return null; })


If i assign that action to obj.MyFunc = new Action(()=>{ console.log(Js.arguments); });


in javascript for that object i can pass anything and deal with it in the one single action...


WHICH IS KOOL... I reduced a bunch of if then on parameter count that hard code the Func<T1... TResult> for each count up to 16... I just know replaced all that code with the following:


 

							Delegate functionDelegate = new Func<dynamic>(() => {
								Root.console.log(Js.arguments);
								dynamic result = null;
								object[] args = null;
								int len = (Js.arguments != null && Js.arguments.length > 0) ? Js.arguments.length : 0;
								if (functionAsync) {
									if (len >= 1) {
										int count = len - 1;
										dynamic error = Js.arguments[len - 1];
										if (count > 0) {
											args = new object[count];
											for (int xx = 0; xx < count; xx++) {
												args[xx] = Js.arguments[xx];
											}
										}
										result = service.CallMethodAsync<dynamic>(functionAttrib, args, error);
									} else {
										throw new Exception("Invalid IRemoteService argument list encounter for method: " + functionName);
									}
								} else {
									if (len >= 2) {
										int count = len - 2;
										dynamic error = Js.arguments[len - 1];
										dynamic success = Js.arguments[len - 2];
										if (count > 0) {
											args = new object[count];
											for (int xx = 0; xx < count; xx++) {
												args[xx] = Js.arguments[xx];
											}
										}
										service.CallMethod<dynamic>(functionAttrib, args, success, error);
									} else {
										throw new Exception("Invalid IRemoteService argument list encounter for method: " + functionName);
									}
								}
								return result;
							});
							if (functionDelegate != null) {
								dynamic_service[functionName] = functionDelegate;
							}

 



THANKS SOO MUCH FOR THE QUICK AND USEFUL INFO... NO NEED __arglist :)


That was the ICING ON THE CAKE for the last piece i needed for my Remote Service Framework...


Kinda like GWT-RPC... I Call it AJAX-RPC


But now all the client code necessary to use AJAX-RPC is the interface for your service on the server.


Example AJAX-RPC interface for DuoCode:


 

using System;
using System.Gateway;
using System.Gateway.Rpc;
using System.Threading.Tasks;

using DuoCode.Dom;
using DuoCode.Runtime;

namespace System.Gateway.Services
{
	public interface IGreetingService : IRemoteService
	{
		[RpcMethod(MethodName = "system.gateway.greetserver")]
		void greetServer(string input, Action<string> success = null, Action<Exception> failure = null);

		[RpcMethod(MethodName = "system.gateway.greetserver")]
		Task<string> greetServerAsync(string input, Action<Exception> exception = null);
	}
}

 

NOTE: Im still waiting on that AWAIT / ASYNC to really top things off for me... As far as the WAY you call Rpc Services


Instead of :


 

var svc = Program.CreateService<IGreetingService>();
svc.greetServer(textToServer, (string result) => {
   alert(result);
}

I will be able todo:

var svc = Program.CreateService<IGreetingService>();
string result = await svc.greetServerAsync(textToServer);

WILL BE WAY KOOLER... I got all the code ready and wrapped with TaskCompletionSource..

Just can't compile with async keyword so i can only use with the ContinueWith 
which is really the same thing as the first CALLBACK function example

 

Js.arguments are always defined and can't be null inside JS function, you can eliminate the null check and access the .length .

Kool... Thanks a bunch. That allow me to make a RemotwService.Create<IService>() factory method for creating remote service where the base RemoteService handle the actual

HTTP calls using a generalized CallMethod or CallMethodAsync. The kool stuff comes in by defining an interface for the actual remote service methods (Just like GWT-RPC). The RemoteService.Create will probe the interface and assign delegates to wrap the underlying CallMethod with the strong parameters defined on the interface... Really kool.


Here is the code i am using to do that... Please tell me if i should be using any other build api (like that Js.arguments) that would be better to do what i am trying to do.


  

Example Usage:
var svc = RemoteService.Create<IGreetingService>();
svc.greetServer(textToServer, (string result) => {
   alert(result);
}
 
Factory Class:

public class RemoteService {
      public void CallMethod {...}
      public Task<T> CallMethodAsync {...}

		public static T Create<T>(string gatewayurl = null, Action<Exception> defaultExceptions = null) where T : IService
		{
			return Create<T, RemoteService>(gatewayurl, defaultExceptions);	
		}
		public static T Create<T, R>(string gatewayurl = null, Action<Exception> defaultExceptions = null) where T : IService where R : RemoteService
		{
			Type type = typeof(T);
			RemoteService service = Activator.CreateInstance(typeof(R), new object[] { gatewayurl, defaultExceptions }).As<R>();
			MethodInfo[] methods = type.GetMethods();
			if (methods != null) {
				dynamic dynamic_service = service.As<dynamic>();
				foreach (MethodInfo method in methods) {
					object[] attributes = method.GetCustomAttributes(typeof(RpcMethodAttribute), false);
					if (attributes != null && attributes.Length > 0) {
						RpcMethodAttribute att = attributes[0].As<RpcMethodAttribute>();
						if (att != null && !string.IsNullOrEmpty(att.MethodName)) {
							int functionParams = 0;
							bool functionAsync = typeof(Task).IsAssignableFrom(method.ReturnType);
							string functionName = string.Format("{0}${1}", type.FullName.Replace(".", "$"), method.Name);
							string functionAttrib = att.MethodName;
							ParameterInfo[] paramItems = method.GetParameters();
							if (paramItems != null) functionParams = paramItems.Count;
							if (functionParams >= 1) {
								ParameterInfo error = paramItems[functionParams - 1];
								if (!typeof(Delegate).IsAssignableFrom(error.ParameterType)) {
									throw new Exception("Invalid remoting IService error argument delegate for method: " + functionName);
								}
							} else {
								throw new Exception("Invalid remoting IService argument list encounter for method: " + functionName);
							}
							if (!functionAsync) {
								if (functionParams >= 2) {
									ParameterInfo success = paramItems[functionParams - 2];
									if (!typeof(Delegate).IsAssignableFrom(success.ParameterType)) {
										throw new Exception("Invalid remoting IService success argument delegate for method: " + functionName);
									}
								} else {
									throw new Exception("Invalid remoting IService argument list encounter for method: " + functionName);
								}
							}
							Delegate functionDelegate = new Func<dynamic>(() => {
								dynamic result = null;
								object[] args = null;
								int len = Js.arguments.length;
								if (functionAsync) {
									if (len >= 1) {
										int count = len - 1;
										dynamic error = Js.arguments[len - 1];
										if (count > 0) {
											args = new object[count];
											for (int xx = 0; xx < count; xx++) {
												args[xx] = Js.arguments[xx];
											}
										}
										result = service.CallMethodAsync<dynamic>(functionAttrib, args, error);
									} else {
										throw new Exception("Invalid remoting IService argument list encounter for method: " + functionName);
									}
								} else {
									if (len >= 2) {
										int count = len - 2;
										dynamic error = Js.arguments[len - 1];
										dynamic success = Js.arguments[len - 2];
										if (count > 0) {
											args = new object[count];
											for (int xx = 0; xx < count; xx++) {
												args[xx] = Js.arguments[xx];
											}
										}
										service.CallMethod<dynamic>(functionAttrib, args, success, error);
									} else {
										throw new Exception("Invalid remoting IService argument list encounter for method: " + functionName);
									}
								}
								return result;
							});
							if (functionDelegate != null) {
								dynamic_service[functionName] = functionDelegate;
							}
						}
					}
				}
			}
			return service.As<T>();
		}
}

  

Login or Signup to post a comment