Skip to content

Commit 9dba825

Browse files
authored
Fixed Extension methods on a string (#832)
* Add ExtensionMethod_OnString_NoParameter * EmptyIfNull(this string s) * Fix
1 parent 1174481 commit 9dba825

File tree

3 files changed

+68
-37
lines changed

3 files changed

+68
-37
lines changed

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -2074,6 +2074,7 @@ private bool TryParseEnumerable(Expression instance, Type elementType, string me
20742074
}
20752075

20762076
args = ParseArgumentList();
2077+
var copyArgs = args.ToArray();
20772078

20782079
// Revert the _it and _parent to the old values.
20792080
_it = outerIt;
@@ -2082,7 +2083,11 @@ private bool TryParseEnumerable(Expression instance, Type elementType, string me
20822083
var theType = type ?? instance.Type;
20832084
if (theType == typeof(string) && _methodFinder.ContainsMethod(theType, methodName, false, instance, ref args))
20842085
{
2085-
// 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.
2086+
// In case the type is a string, and does contain the methodName (like "IndexOf") then:
2087+
// - restore the args
2088+
// - set the expression to null
2089+
// - return false to indicate that the methodName is not an Enumerable method
2090+
args = copyArgs;
20862091
expression = null;
20872092
return false;
20882093
}
@@ -2235,7 +2240,7 @@ private Expression[] ParseArgumentList()
22352240
_textParser.ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
22362241
_textParser.NextToken();
22372242

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

22402245
_textParser.ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
22412246
_textParser.NextToken();

src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs

+25-27
Original file line numberDiff line numberDiff line change
@@ -210,19 +210,19 @@ public int FindBestMethodBasedOnArguments(IEnumerable<MethodBase> methods, ref E
210210

211211
public int FindIndexer(Type type, Expression[] args, out MethodBase? method)
212212
{
213-
foreach (Type t in SelfAndBaseTypes(type))
213+
foreach (var t in SelfAndBaseTypes(type))
214214
{
215-
MemberInfo[] members = t.GetDefaultMembers();
215+
var members = t.GetDefaultMembers();
216216
if (members.Length != 0)
217217
{
218218
IEnumerable<MethodBase> methods = members.OfType<PropertyInfo>().
219219
#if !(NETFX_CORE || WINDOWS_APP || UAP10_0 || NETSTANDARD)
220-
Select(p => (MethodBase)p.GetGetMethod()).
221-
Where(m => m != null);
220+
Select(p => (MethodBase?)p.GetGetMethod()).
221+
OfType<MethodBase>();
222222
#else
223223
Select(p => (MethodBase)p.GetMethod);
224224
#endif
225-
int count = FindBestMethodBasedOnArguments(methods, ref args, out method);
225+
var count = FindBestMethodBasedOnArguments(methods, ref args, out method);
226226
if (count != 0)
227227
{
228228
return count;
@@ -289,7 +289,7 @@ private bool IsApplicable(MethodData method, Expression[] args)
289289
if (methodParameter.IsOut && args[i] is ParameterExpression parameterExpression)
290290
{
291291
#if NET35
292-
return false;
292+
return false;
293293
#else
294294
if (!parameterExpression.IsByRef)
295295
{
@@ -318,33 +318,31 @@ private bool IsApplicable(MethodData method, Expression[] args)
318318

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

327-
bool better = false;
328-
for (int i = 0; i < args.Length; i++)
327+
var better = false;
328+
for (var i = 0; i < args.Length; i++)
329329
{
330-
CompareConversionType result = CompareConversions(args[i].Type, first.Parameters[i].ParameterType, second.Parameters[i].ParameterType);
331-
332-
// If second is better, return false
333-
if (result == CompareConversionType.Second)
334-
{
335-
return false;
336-
}
337-
338-
// If first is better, return true
339-
if (result == CompareConversionType.First)
340-
{
341-
return true;
342-
}
330+
var result = CompareConversions(args[i].Type, first.Parameters[i].ParameterType, second.Parameters[i].ParameterType);
343331

344-
// If both are same, just set better to true and continue
345-
if (result == CompareConversionType.Both)
332+
switch (result)
346333
{
347-
better = true;
334+
// If second is better, return false
335+
case CompareConversionType.Second:
336+
return false;
337+
338+
// If first is better, return true
339+
case CompareConversionType.First:
340+
return true;
341+
342+
// If both are same, just set better to true and continue
343+
case CompareConversionType.Both:
344+
better = true;
345+
break;
348346
}
349347
}
350348

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

372-
bool firstIsCompatibleWithSecond = TypeHelper.IsCompatibleWith(first, second);
373-
bool secondIsCompatibleWithFirst = TypeHelper.IsCompatibleWith(second, first);
370+
var firstIsCompatibleWithSecond = TypeHelper.IsCompatibleWith(first, second);
371+
var secondIsCompatibleWithFirst = TypeHelper.IsCompatibleWith(second, first);
374372

375373
if (firstIsCompatibleWithSecond && !secondIsCompatibleWithFirst)
376374
{

test/System.Linq.Dynamic.Core.Tests/Parser/DynamicLinqTypeTest.cs

+36-8
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,29 @@ public static string[] ConvertToArray(int a, params string[] values)
2828
return values.ToArray();
2929
}
3030

31-
public static int IncrementMe(this int values)
31+
public static int IncrementMe(this int value)
3232
{
33-
return values + 1;
33+
return value + 1;
3434
}
3535

36-
public static int IncrementMe(this int values, int y)
36+
public static int IncrementMe(this int value, int y)
3737
{
38-
return values + y;
38+
return value + y;
3939
}
4040

41-
public static int IncrementMeAlso(this int values)
41+
public static int IncrementMeAlso(this int value)
4242
{
43-
return values + 1;
43+
return value + 1;
44+
}
45+
46+
public static string EmptyIfNull(this string? s)
47+
{
48+
return s ?? string.Empty;
49+
}
50+
51+
public static string DefaultIfNull(this string? s, string defaultValue)
52+
{
53+
return s ?? defaultValue;
4454
}
4555
}
4656

@@ -161,7 +171,25 @@ public void ParamArray_Array()
161171
}
162172

163173
[Fact]
164-
public void ExtensionMethod_NoParameter()
174+
public void ExtensionMethod_OnString_NoParameter()
175+
{
176+
var list = new[] { "a", "", null }.AsQueryable();
177+
var result = list.Select("it.EmptyIfNull()").ToDynamicList<string?>();
178+
179+
result.Should().Equal("a", "", "");
180+
}
181+
182+
[Fact]
183+
public void ExtensionMethod_OnString_OneParameter()
184+
{
185+
var list = new[] { "a", "", null }.AsQueryable();
186+
var result = list.Select("it.DefaultIfNull(\"x\")").ToDynamicList<string?>();
187+
188+
result.Should().Equal("a", "", "x");
189+
}
190+
191+
[Fact]
192+
public void ExtensionMethod_OnInt_NoParameter()
165193
{
166194
var list = new[] { new EntityValue { ValueInt = 1 }, new EntityValue { ValueInt = 2 } }.AsQueryable();
167195
var result = list.Select("ValueInt.IncrementMe()").ToDynamicList<int>();
@@ -172,7 +200,7 @@ public void ExtensionMethod_NoParameter()
172200
}
173201

174202
[Fact]
175-
public void ExtensionMethod_SingleParameter()
203+
public void ExtensionMethod_OnInt_SingleParameter()
176204
{
177205
var list = new[] { new EntityValue { ValueInt = 1 }, new EntityValue { ValueInt = 2 } }.AsQueryable();
178206
var result = list.Select("ValueInt.IncrementMe(5)").ToDynamicList<int>();

0 commit comments

Comments
 (0)