Skip to content

Commit

Permalink
Allow a params parameter to have a generic type (#192)
Browse files Browse the repository at this point in the history
* Allow a params parameter to have a generic type.

* Throw a ParseException when a method's type parameters can't be inferred.
  • Loading branch information
metoule authored Nov 23, 2021
1 parent 3fd6251 commit 0cdc344
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 9 deletions.
32 changes: 30 additions & 2 deletions src/DynamicExpresso.Core/Parsing/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1943,7 +1943,15 @@ private static bool CheckIfMethodIsApplicableAndPrepareIt(MethodData method, Exp

if (paramsArrayTypeFound != null)
{
var promoted = PromoteExpression(currentArgument, paramsArrayTypeFound.GetElementType(), true);
var paramsArrayElementType = paramsArrayTypeFound.GetElementType();
if (paramsArrayElementType.IsGenericParameter)
{
paramsArrayPromotedArgument = paramsArrayPromotedArgument ?? new List<Expression>();
paramsArrayPromotedArgument.Add(currentArgument);
continue;
}

var promoted = PromoteExpression(currentArgument, paramsArrayElementType, true);
if (promoted != null)
{
paramsArrayPromotedArgument = paramsArrayPromotedArgument ?? new List<Expression>();
Expand All @@ -1960,7 +1968,17 @@ private static bool CheckIfMethodIsApplicableAndPrepareIt(MethodData method, Exp
method.HasParamsArray = true;
var paramsArrayElementType = paramsArrayTypeFound.GetElementType();
if (paramsArrayElementType == null)
throw new Exception("Type is not an array, element not found");
throw CreateParseException(-1, ErrorMessages.ParamsArrayTypeNotAnArray);

if (paramsArrayElementType.IsGenericParameter)
{
var actualTypes = paramsArrayPromotedArgument.Select(_ => _.Type).Distinct().ToArray();
if (actualTypes.Length != 1)
throw CreateParseException(-1, ErrorMessages.MethodTypeParametersCantBeInferred, method.MethodBase);

paramsArrayElementType = actualTypes[0];
}

promotedArgs.Add(Expression.NewArrayInit(paramsArrayElementType, paramsArrayPromotedArgument));
}

Expand Down Expand Up @@ -2030,6 +2048,16 @@ private static Dictionary<string, Type> ExtractActualGenericArguments(
if (!actualType.IsGenericParameter)
extractedGenericTypes[requestedType.Name] = actualType;
}
else if (requestedType.IsArray && actualType.IsArray)
{
var innerGenericTypes = ExtractActualGenericArguments(
new[] { requestedType.GetElementType() },
new[] { actualType.GetElementType()
});

foreach (var innerGenericType in innerGenericTypes)
extractedGenericTypes[innerGenericType.Key] = innerGenericType.Value;
}
else if (requestedType.ContainsGenericParameters)
{
if (actualType.IsGenericParameter)
Expand Down
32 changes: 27 additions & 5 deletions src/DynamicExpresso.Core/Resources/ErrorMessages.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/DynamicExpresso.Core/Resources/ErrorMessages.resx
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,10 @@
<data name="CloseTypeArgumentListExpected" xml:space="preserve">
<value>'&gt;' expected</value>
</data>
<data name="MethodTypeParametersCantBeInferred" xml:space="preserve">
<value>The type arguments for method '{0}' cannot be inferred from the usage.</value>
</data>
<data name="ParamsArrayTypeNotAnArray" xml:space="preserve">
<value>Params array type is not an array, element not found</value>
</data>
</root>
27 changes: 25 additions & 2 deletions test/DynamicExpresso.UnitTest/MemberInvocationTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using DynamicExpresso.Exceptions;
using NUnit.Framework;

namespace DynamicExpresso.UnitTest
Expand Down Expand Up @@ -370,7 +371,21 @@ public void Overload_paramsArray_methods_with_compatible_type_params()
target.SetVariable("x", x);
Assert.AreEqual(3, target.Eval("x.OverloadMethodWithParamsArray(2, 3, 1)"));
}



[Test]
public void Generic_method_with_params()
{
var target = new Interpreter();
target.Reference(typeof(Utils));

var listInt = target.Eval<List<int>>("Utils.Array(1, 2, 3)");
Assert.AreEqual(new[] { 1, 2, 3 }, listInt);

// type parameter can't be inferred from usage
Assert.Throws<ParseException>(() => target.Eval<List<int>>("Utils.Array(1,\"str\", 3)"));
}

[Test]
public void Method_with_optional_param()
{
Expand Down Expand Up @@ -555,5 +570,13 @@ public string AMethod()
return "Ciao mondo";
}
}

private static class Utils
{
public static List<T> Array<T>(params T[] array)
{
return new List<T>(array);
}
}
}
}

0 comments on commit 0cdc344

Please sign in to comment.