Skip to content

Commit

Permalink
Fixed Extension methods on a string (#832)
Browse files Browse the repository at this point in the history
* Add ExtensionMethod_OnString_NoParameter

* EmptyIfNull(this string s)

* Fix
  • Loading branch information
StefH authored Jul 29, 2024
1 parent 1174481 commit 9dba825
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 37 deletions.
9 changes: 7 additions & 2 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2074,6 +2074,7 @@ private bool TryParseEnumerable(Expression instance, Type elementType, string me
}

args = ParseArgumentList();
var copyArgs = args.ToArray();

// Revert the _it and _parent to the old values.
_it = outerIt;
Expand All @@ -2082,7 +2083,11 @@ private bool TryParseEnumerable(Expression instance, Type elementType, string me
var theType = type ?? instance.Type;
if (theType == typeof(string) && _methodFinder.ContainsMethod(theType, methodName, false, instance, ref args))
{
// In case the type is a string, and does contain the methodName (like "IndexOf"), then return false to indicate that the methodName is not an Enumerable method.
// In case the type is a string, and does contain the methodName (like "IndexOf") then:
// - restore the args
// - set the expression to null
// - return false to indicate that the methodName is not an Enumerable method
args = copyArgs;
expression = null;
return false;
}
Expand Down Expand Up @@ -2235,7 +2240,7 @@ private Expression[] ParseArgumentList()
_textParser.ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
_textParser.NextToken();

var args = _textParser.CurrentToken.Id != TokenId.CloseParen ? ParseArguments() : new Expression[0];
var args = _textParser.CurrentToken.Id != TokenId.CloseParen ? ParseArguments() : [];

_textParser.ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
_textParser.NextToken();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,19 +210,19 @@ public int FindBestMethodBasedOnArguments(IEnumerable<MethodBase> methods, ref E

public int FindIndexer(Type type, Expression[] args, out MethodBase? method)
{
foreach (Type t in SelfAndBaseTypes(type))
foreach (var t in SelfAndBaseTypes(type))
{
MemberInfo[] members = t.GetDefaultMembers();
var members = t.GetDefaultMembers();
if (members.Length != 0)
{
IEnumerable<MethodBase> methods = members.OfType<PropertyInfo>().
#if !(NETFX_CORE || WINDOWS_APP || UAP10_0 || NETSTANDARD)
Select(p => (MethodBase)p.GetGetMethod()).
Where(m => m != null);
Select(p => (MethodBase?)p.GetGetMethod()).
OfType<MethodBase>();
#else
Select(p => (MethodBase)p.GetMethod);
#endif
int count = FindBestMethodBasedOnArguments(methods, ref args, out method);
var count = FindBestMethodBasedOnArguments(methods, ref args, out method);
if (count != 0)
{
return count;
Expand Down Expand Up @@ -289,7 +289,7 @@ private bool IsApplicable(MethodData method, Expression[] args)
if (methodParameter.IsOut && args[i] is ParameterExpression parameterExpression)
{
#if NET35
return false;
return false;
#else
if (!parameterExpression.IsByRef)
{
Expand Down Expand Up @@ -318,33 +318,31 @@ private bool IsApplicable(MethodData method, Expression[] args)

private static bool FirstIsBetterThanSecond(Expression[] args, MethodData first, MethodData second)
{
// If args count is 0 -> parameterless method is better than method method with parameters
// If args count is 0 -> parameterless method is better than a method with parameters
if (args.Length == 0)
{
return first.Parameters.Length == 0 && second.Parameters.Length != 0;
}

bool better = false;
for (int i = 0; i < args.Length; i++)
var better = false;
for (var i = 0; i < args.Length; i++)
{
CompareConversionType result = CompareConversions(args[i].Type, first.Parameters[i].ParameterType, second.Parameters[i].ParameterType);

// If second is better, return false
if (result == CompareConversionType.Second)
{
return false;
}

// If first is better, return true
if (result == CompareConversionType.First)
{
return true;
}
var result = CompareConversions(args[i].Type, first.Parameters[i].ParameterType, second.Parameters[i].ParameterType);

// If both are same, just set better to true and continue
if (result == CompareConversionType.Both)
switch (result)
{
better = true;
// If second is better, return false
case CompareConversionType.Second:
return false;

// If first is better, return true
case CompareConversionType.First:
return true;

// If both are same, just set better to true and continue
case CompareConversionType.Both:
better = true;
break;
}
}

Expand All @@ -369,8 +367,8 @@ private static CompareConversionType CompareConversions(Type source, Type first,
return CompareConversionType.Second;
}

bool firstIsCompatibleWithSecond = TypeHelper.IsCompatibleWith(first, second);
bool secondIsCompatibleWithFirst = TypeHelper.IsCompatibleWith(second, first);
var firstIsCompatibleWithSecond = TypeHelper.IsCompatibleWith(first, second);
var secondIsCompatibleWithFirst = TypeHelper.IsCompatibleWith(second, first);

if (firstIsCompatibleWithSecond && !secondIsCompatibleWithFirst)
{
Expand Down
44 changes: 36 additions & 8 deletions test/System.Linq.Dynamic.Core.Tests/Parser/DynamicLinqTypeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,29 @@ public static string[] ConvertToArray(int a, params string[] values)
return values.ToArray();
}

public static int IncrementMe(this int values)
public static int IncrementMe(this int value)
{
return values + 1;
return value + 1;
}

public static int IncrementMe(this int values, int y)
public static int IncrementMe(this int value, int y)
{
return values + y;
return value + y;
}

public static int IncrementMeAlso(this int values)
public static int IncrementMeAlso(this int value)
{
return values + 1;
return value + 1;
}

public static string EmptyIfNull(this string? s)
{
return s ?? string.Empty;
}

public static string DefaultIfNull(this string? s, string defaultValue)
{
return s ?? defaultValue;
}
}

Expand Down Expand Up @@ -161,7 +171,25 @@ public void ParamArray_Array()
}

[Fact]
public void ExtensionMethod_NoParameter()
public void ExtensionMethod_OnString_NoParameter()
{
var list = new[] { "a", "", null }.AsQueryable();
var result = list.Select("it.EmptyIfNull()").ToDynamicList<string?>();

result.Should().Equal("a", "", "");
}

[Fact]
public void ExtensionMethod_OnString_OneParameter()
{
var list = new[] { "a", "", null }.AsQueryable();
var result = list.Select("it.DefaultIfNull(\"x\")").ToDynamicList<string?>();

result.Should().Equal("a", "", "x");
}

[Fact]
public void ExtensionMethod_OnInt_NoParameter()
{
var list = new[] { new EntityValue { ValueInt = 1 }, new EntityValue { ValueInt = 2 } }.AsQueryable();
var result = list.Select("ValueInt.IncrementMe()").ToDynamicList<int>();
Expand All @@ -172,7 +200,7 @@ public void ExtensionMethod_NoParameter()
}

[Fact]
public void ExtensionMethod_SingleParameter()
public void ExtensionMethod_OnInt_SingleParameter()
{
var list = new[] { new EntityValue { ValueInt = 1 }, new EntityValue { ValueInt = 2 } }.AsQueryable();
var result = list.Select("ValueInt.IncrementMe(5)").ToDynamicList<int>();
Expand Down

0 comments on commit 9dba825

Please sign in to comment.