Skip to content

Commit

Permalink
Added support for optional parameters in method calls (#44)
Browse files Browse the repository at this point in the history
* Added support for optional parameters in method calls
Handles a possible null reference exception in InvokePropertyOrField
Handles a possible AmbiguousMatchException in InvokePropertyOrField
* Param Branch merged, Start of improvements and InvokeMethod singleton
* New interface IInvokeMethod implimentation of changes to support this new interface. InvokeMethod is now a singleton passed appropiately to dependants.

---------

Co-authored-by: Richard Spurgeon <richard.spurgeon@paramet.co.uk>
  • Loading branch information
microspud and Richard Spurgeon committed Dec 31, 2023
1 parent 249ae4a commit 7d3f2c8
Show file tree
Hide file tree
Showing 6 changed files with 419 additions and 69 deletions.
59 changes: 56 additions & 3 deletions src/NReco.LambdaParser.Tests/LambdaParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics;
using System.Text;

using Xunit;

namespace NReco.Linq.Tests {
Expand Down Expand Up @@ -130,6 +128,23 @@ public class LambdaParserTests {
Assert.Equal(new TimeSpan(1,0,0,0), lambdaParser.Eval("twoDays + -oneDay", varContext));
Assert.Equal(new TimeSpan(1,0,0,0).Negate(), lambdaParser.Eval("oneDay - twoDays", varContext));
Assert.Equal(new TimeSpan(1,0,0,0).Negate(), lambdaParser.Eval("-twoDays + oneDay", varContext));

//Use new invoker
lambdaParser = new LambdaParser(OptionsParamsInvokeMethod.Instance);

Assert.True((bool)lambdaParser.Eval("testObj.OptionalParam(true,true)", varContext));
Assert.False((bool)lambdaParser.Eval("testObj.OptionalParam(true,true,false)", varContext));

Assert.True((bool)lambdaParser.Eval("testObj.OptionalParam2(true,true)", varContext));
Assert.False((bool)lambdaParser.Eval("testObj.OptionalParam2(true,true,true,\"fail\")", varContext));

Assert.True((bool)lambdaParser.Eval("testObj.TestShadowMethod()", varContext));

Assert.True((bool)lambdaParser.Eval("testObj.TestShadowProperty", varContext));

Assert.Equal("Test123ThisIsaTest",(string)lambdaParser.Eval("testObj.ParamMethodTest(\"Test\",123,\"This\",\"Is\",\"a\",\"Test\")", varContext));
Assert.Equal("Today is Saturday, Day 9 of December",lambdaParser.Eval("testObj.Format(\"Today is {0}, Day {1} of {2}\",\"Saturday\",9,\"December\")", varContext));

}

[Fact]
Expand Down Expand Up @@ -196,7 +211,19 @@ public class LambdaParserTests {
}


public class TestClass {
public class TestBaseClass
{

public bool TestShadowMethod()
{
return false;
}

public int TestShadowProperty { get { return 0; } }

}

public class TestClass : TestBaseClass {

public int IntProp { get { return 1; } }

Expand Down Expand Up @@ -233,6 +260,32 @@ public class TestClass {
};
}

public bool OptionalParam(bool First, bool Second, bool Third = true)
{
return First & Second & Third;
}

public bool OptionalParam2(bool First, bool Second, bool Third = true, string Forth = "pass")
{
return First & Second & Third & Forth == "pass";
}

public new bool TestShadowMethod()
{
return true;
}

public new bool TestShadowProperty { get { return true; } }

public string ParamMethodTest(string First, int Second, params string[] args)
{
return string.Concat(First, Second) + string.Concat(args);
}

public string Format(string format,params object[] args)
{
return String.Format(format, args);
}

}

Expand Down
28 changes: 14 additions & 14 deletions src/NReco.LambdaParser/InvokeMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ namespace NReco {
/// <summary>
/// Invoke object's method that is most compatible with provided arguments
/// </summary>
internal class InvokeMethod {
internal class InvokeMethod : Linq.IInvokeMethod {

public object TargetObject { get; set; }
internal readonly static InvokeMethod _Instance = new InvokeMethod();

public string MethodName { get; set; }

public InvokeMethod(object o, string methodName) {
TargetObject = o;
MethodName = methodName;
public static Linq.IInvokeMethod Instance
{
get {
return _Instance;
}
}

protected MethodInfo FindMethod(Type[] argTypes) {
protected MethodInfo FindMethod(object TargetObject, string MethodName, Type[] argTypes) {
if (TargetObject is Type) {
// static method
#if NET40
Expand All @@ -50,7 +50,7 @@ internal class InvokeMethod {
#endif
}

protected IEnumerable<MethodInfo> GetAllMethods() {
protected IEnumerable<MethodInfo> GetAllMethods(object TargetObject) {
if (TargetObject is Type) {
#if NET40
return ((Type)TargetObject).GetMethods(BindingFlags.Static | BindingFlags.Public);
Expand All @@ -65,16 +65,16 @@ internal class InvokeMethod {
#endif
}

public object Invoke(object[] args) {
public object Invoke(object TargetObject, string MethodName, object[] args) {
Type[] argTypes = new Type[args.Length];
for (int i = 0; i < argTypes.Length; i++)
argTypes[i] = args[i] != null ? args[i].GetType() : typeof(object);

// strict matching first
MethodInfo targetMethodInfo = FindMethod(argTypes);
MethodInfo targetMethodInfo = FindMethod(TargetObject, MethodName, argTypes);
// fuzzy matching
if (targetMethodInfo==null) {
var methods = GetAllMethods();
var methods = GetAllMethods(TargetObject);

foreach (var m in methods)
if (m.Name==MethodName &&
Expand All @@ -92,7 +92,7 @@ internal class InvokeMethod {
throw new MissingMemberException(
(TargetObject is Type ? (Type)TargetObject : TargetObject.GetType()).FullName+"."+MethodName );
}
object[] argValues = PrepareActualValues(targetMethodInfo.GetParameters(),args);
object[] argValues = PrepareActualValues(MethodName,targetMethodInfo.GetParameters(),args);
object res = null;
try {
res = targetMethodInfo.Invoke( TargetObject is Type ? null : TargetObject, argValues);
Expand Down Expand Up @@ -143,7 +143,7 @@ internal class InvokeMethod {
}


protected object[] PrepareActualValues(ParameterInfo[] paramsInfo, object[] values) {
protected object[] PrepareActualValues(string MethodName, ParameterInfo[] paramsInfo, object[] values) {
object[] res = new object[paramsInfo.Length];
for (int i=0; i<paramsInfo.Length; i++) {
if (values[i]==null || IsInstanceOfType( paramsInfo[i].ParameterType, values[i])) {
Expand Down
25 changes: 25 additions & 0 deletions src/NReco.LambdaParser/Linq/IInvokeMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace NReco.Linq
{

/// <summary>
/// Exposes a method that allows the invoke of a method within an object
/// </summary>
/// <remarks>
/// Interface to allow different implimentations of invoke method with different capabilities.
/// ensures backwards compatibility and behavour.
/// </remarks>
public interface IInvokeMethod
{

/// <summary>
/// Invokes a method within an object (targetobject), given a set of arguments / parameters passed to method
/// </summary>
/// <returns>An object reference to the return value of the method</returns>
object Invoke(object TargetObject, string MethodName, object[] args);

}
}
Loading

0 comments on commit 7d3f2c8

Please sign in to comment.