Skip to content

Commit 782bbb5

Browse files
committed
.
1 parent 922e8e9 commit 782bbb5

File tree

5 files changed

+176
-163
lines changed

5 files changed

+176
-163
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Collections.Generic;
2+
3+
namespace System.Linq.Dynamic.Core.Extensions;
4+
5+
internal static class ListExtensions
6+
{
7+
internal static void AddIfNotNull<T>(this IList<T> list, T? value)
8+
{
9+
if (value != null)
10+
{
11+
list.Add(value);
12+
}
13+
}
14+
}

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

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,7 +1494,10 @@ private Expression CreateArrayInitializerExpression(List<Expression> expressions
14941494

14951495
if (newType != null)
14961496
{
1497-
return Expression.NewArrayInit(newType, expressions.Select(expression => _parsingConfig.ExpressionPromoter.Promote(expression, newType, true, true)));
1497+
var promotedExpressions = expressions
1498+
.Select(expression => _parsingConfig.ExpressionPromoter.Promote(expression, newType, true, true))
1499+
.OfType<Expression>();
1500+
return Expression.NewArrayInit(newType, promotedExpressions);
14981501
}
14991502

15001503
return Expression.NewArrayInit(expressions.All(expression => expression.Type == expressions[0].Type) ? expressions[0].Type : typeof(object), expressions);
@@ -1551,24 +1554,24 @@ private Expression CreateNewExpression(List<DynamicProperty> properties, List<Ex
15511554
{
15521555
var bindParametersSequentially = !properties.All(p => constructorParameters
15531556
.Any(cp => cp.Name == p.Name && (cp.ParameterType == p.Type || p.Type == Nullable.GetUnderlyingType(cp.ParameterType))));
1554-
var expressionsPromoted = new List<Expression?>();
1557+
var expressionsPromoted = new List<Expression>();
15551558

15561559
// Loop all expressions and promote if needed
15571560
for (int i = 0; i < constructorParameters.Length; i++)
15581561
{
15591562
if (bindParametersSequentially)
15601563
{
1561-
expressionsPromoted.Add(_parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyTypes[i], true, true));
1564+
expressionsPromoted.AddIfNotNull(_parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyTypes[i], true, true));
15621565
}
15631566
else
15641567
{
1565-
Type propertyType = constructorParameters[i].ParameterType;
1568+
var propertyType = constructorParameters[i].ParameterType;
15661569
var cParameterName = constructorParameters[i].Name;
15671570
var propertyAndIndex = properties.Select((p, index) => new { p, index })
15681571
.First(p => p.p.Name == cParameterName && (p.p.Type == propertyType || p.p.Type == Nullable.GetUnderlyingType(propertyType)));
1569-
1572+
15701573
// Promote from Type to Nullable Type if needed
1571-
expressionsPromoted.Add(_parsingConfig.ExpressionPromoter.Promote(expressions[propertyAndIndex.index], propertyType, true, true));
1574+
expressionsPromoted.AddIfNotNull(_parsingConfig.ExpressionPromoter.Promote(expressions[propertyAndIndex.index], propertyType, true, true));
15721575
}
15731576
}
15741577

@@ -1584,6 +1587,7 @@ private Expression CreateNewExpression(List<DynamicProperty> properties, List<Ex
15841587
// Promote from Type to Nullable Type if needed
15851588
var expressionsPromoted = exactConstructor.GetParameters()
15861589
.Select((t, i) => _parsingConfig.ExpressionPromoter.Promote(expressions[i], t.ParameterType, true, true))
1590+
.OfType<Expression>()
15871591
.ToArray();
15881592

15891593
return Expression.New(exactConstructor, expressionsPromoted);
@@ -1661,14 +1665,14 @@ private Expression ParseTypeAccess(Type type, bool getNext)
16611665
}
16621666

16631667
// This is a shorthand for explicitly converting a string to something
1664-
bool shorthand = _textParser.CurrentToken.Id == TokenId.StringLiteral;
1665-
if (_textParser.CurrentToken.Id == TokenId.OpenParen || shorthand)
1668+
var isShorthand = _textParser.CurrentToken.Id == TokenId.StringLiteral;
1669+
if (_textParser.CurrentToken.Id == TokenId.OpenParen || isShorthand)
16661670
{
16671671
Expression[] args;
1668-
if (shorthand)
1672+
if (isShorthand)
16691673
{
16701674
var expressionOrType = ParseStringLiteral(true);
1671-
args = new[] { expressionOrType.First };
1675+
args = [expressionOrType.First];
16721676
}
16731677
else
16741678
{
@@ -1685,7 +1689,7 @@ private Expression ParseTypeAccess(Type type, bool getNext)
16851689
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
16861690
if (args.Length == 1 && (args[0] == null || args[0] is ConstantExpression) && TryGenerateConversion(args[0], type, out var generatedExpression))
16871691
{
1688-
return generatedExpression!;
1692+
return generatedExpression;
16891693
}
16901694

16911695
// If only 1 argument, and if the type is a ValueType and argType is also a ValueType, just Convert
Lines changed: 93 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,136 +1,135 @@
11
using System.Linq.Expressions;
22
using System.Reflection;
33

4-
namespace System.Linq.Dynamic.Core.Parser
4+
namespace System.Linq.Dynamic.Core.Parser;
5+
6+
/// <summary>
7+
/// ExpressionPromoter
8+
/// </summary>
9+
public class ExpressionPromoter : IExpressionPromoter
510
{
11+
private readonly NumberParser _numberParser;
12+
private readonly ConstantExpressionHelper _constantExpressionHelper;
13+
614
/// <summary>
7-
/// ExpressionPromoter
15+
/// Initializes a new instance of the <see cref="ExpressionPromoter"/> class.
816
/// </summary>
9-
public class ExpressionPromoter : IExpressionPromoter
17+
/// <param name="config">The ParsingConfig.</param>
18+
public ExpressionPromoter(ParsingConfig config)
1019
{
11-
private readonly NumberParser _numberParser;
12-
private readonly ConstantExpressionHelper _constantExpressionHelper;
20+
_numberParser = new NumberParser(config);
21+
_constantExpressionHelper = ConstantExpressionHelperFactory.GetInstance(config);
22+
}
1323

14-
/// <summary>
15-
/// Initializes a new instance of the <see cref="ExpressionPromoter"/> class.
16-
/// </summary>
17-
/// <param name="config">The ParsingConfig.</param>
18-
public ExpressionPromoter(ParsingConfig config)
24+
/// <inheritdoc />
25+
public virtual Expression? Promote(Expression sourceExpression, Type type, bool exact, bool convertExpression)
26+
{
27+
Type returnType;
28+
if (sourceExpression is LambdaExpression lambdaExpression)
1929
{
20-
_numberParser = new NumberParser(config);
21-
_constantExpressionHelper = ConstantExpressionHelperFactory.GetInstance(config);
30+
returnType = lambdaExpression.GetReturnType();
2231
}
23-
24-
/// <inheritdoc />
25-
public virtual Expression? Promote(Expression expr, Type type, bool exact, bool convertExpr)
32+
else
2633
{
27-
Type returnType;
28-
if (expr is LambdaExpression lambdaExpression)
29-
{
30-
returnType = lambdaExpression.GetReturnType();
31-
}
32-
else
33-
{
34-
returnType = expr.Type;
35-
}
34+
returnType = sourceExpression.Type;
35+
}
3636

37-
if (returnType == type || type.IsGenericParameter)
38-
{
39-
return expr;
40-
}
37+
if (returnType == type || type.IsGenericParameter)
38+
{
39+
return sourceExpression;
40+
}
4141

42-
if (expr is ConstantExpression ce)
42+
if (sourceExpression is ConstantExpression ce)
43+
{
44+
if (Constants.IsNull(ce))
4345
{
44-
if (Constants.IsNull(ce))
46+
if (!type.GetTypeInfo().IsValueType || TypeHelper.IsNullableType(type))
4547
{
46-
if (!type.GetTypeInfo().IsValueType || TypeHelper.IsNullableType(type))
47-
{
48-
return Expression.Constant(null, type);
49-
}
48+
return Expression.Constant(null, type);
5049
}
51-
else
50+
}
51+
else
52+
{
53+
if (_constantExpressionHelper.TryGetText(ce, out var text))
5254
{
53-
if (_constantExpressionHelper.TryGetText(ce, out var text))
54-
{
55-
Type target = TypeHelper.GetNonNullableType(type);
56-
object? value = null;
55+
Type target = TypeHelper.GetNonNullableType(type);
56+
object? value = null;
5757

5858
#if !(UAP10_0 || NETSTANDARD)
59-
switch (Type.GetTypeCode(ce.Type))
60-
{
61-
case TypeCode.Int32:
62-
case TypeCode.UInt32:
63-
case TypeCode.Int64:
64-
case TypeCode.UInt64:
65-
value = _numberParser.ParseNumber(text, target);
66-
67-
// Make sure an enum value stays an enum value
68-
if (target.IsEnum)
69-
{
70-
value = Enum.ToObject(target, value!);
71-
}
72-
break;
73-
74-
case TypeCode.Double:
75-
if (target == typeof(decimal) || target == typeof(double))
76-
{
77-
value = _numberParser.ParseNumber(text, target);
78-
}
79-
break;
59+
switch (Type.GetTypeCode(ce.Type))
60+
{
61+
case TypeCode.Int32:
62+
case TypeCode.UInt32:
63+
case TypeCode.Int64:
64+
case TypeCode.UInt64:
65+
value = _numberParser.ParseNumber(text, target);
8066

81-
case TypeCode.String:
82-
TypeHelper.TryParseEnum(text, target, out value);
83-
break;
84-
}
85-
#else
86-
if (ce.Type == typeof(int) || ce.Type == typeof(uint) || ce.Type == typeof(long) || ce.Type == typeof(ulong))
87-
{
88-
// If target is an enum value, just use the Value from the ConstantExpression
89-
if (target.GetTypeInfo().IsEnum)
90-
{
91-
value = Enum.ToObject(target, ce.Value);
92-
}
93-
else
67+
// Make sure an enum value stays an enum value
68+
if (target.IsEnum)
9469
{
95-
value = _numberParser.ParseNumber(text!, target);
70+
value = Enum.ToObject(target, value!);
9671
}
97-
}
98-
else if (ce.Type == typeof(double))
99-
{
72+
break;
73+
74+
case TypeCode.Double:
10075
if (target == typeof(decimal) || target == typeof(double))
10176
{
10277
value = _numberParser.ParseNumber(text, target);
10378
}
79+
break;
80+
81+
case TypeCode.String:
82+
TypeHelper.TryParseEnum(text, target, out value);
83+
break;
84+
}
85+
#else
86+
if (ce.Type == typeof(int) || ce.Type == typeof(uint) || ce.Type == typeof(long) || ce.Type == typeof(ulong))
87+
{
88+
// If target is an enum value, just use the Value from the ConstantExpression
89+
if (target.GetTypeInfo().IsEnum)
90+
{
91+
value = Enum.ToObject(target, ce.Value);
10492
}
105-
else if (ce.Type == typeof(string) && TypeHelper.TryParseEnum(text, target, out value))
93+
else
10694
{
107-
// Empty if
95+
value = _numberParser.ParseNumber(text!, target);
10896
}
109-
#endif
110-
if (value != null)
97+
}
98+
else if (ce.Type == typeof(double))
99+
{
100+
if (target == typeof(decimal) || target == typeof(double))
111101
{
112-
return Expression.Constant(value, type);
102+
value = _numberParser.ParseNumber(text, target);
113103
}
114104
}
105+
else if (ce.Type == typeof(string) && TypeHelper.TryParseEnum(text, target, out value))
106+
{
107+
// Empty if
108+
}
109+
#endif
110+
if (value != null)
111+
{
112+
return Expression.Constant(value, type);
113+
}
115114
}
116115
}
116+
}
117117

118-
if (TypeHelper.IsCompatibleWith(returnType, type))
118+
if (TypeHelper.IsCompatibleWith(returnType, type))
119+
{
120+
if (type == typeof(decimal) && TypeHelper.IsEnumType(sourceExpression.Type))
119121
{
120-
if (type == typeof(decimal) && TypeHelper.IsEnumType(expr.Type))
121-
{
122-
return Expression.Convert(Expression.Convert(expr, Enum.GetUnderlyingType(expr.Type)), type);
123-
}
124-
125-
if (type.GetTypeInfo().IsValueType || exact || expr.Type.GetTypeInfo().IsValueType && convertExpr)
126-
{
127-
return Expression.Convert(expr, type);
128-
}
122+
return Expression.Convert(Expression.Convert(sourceExpression, Enum.GetUnderlyingType(sourceExpression.Type)), type);
123+
}
129124

130-
return expr;
125+
if (type.GetTypeInfo().IsValueType || exact || sourceExpression.Type.GetTypeInfo().IsValueType && convertExpression)
126+
{
127+
return Expression.Convert(sourceExpression, type);
131128
}
132129

133-
return null;
130+
return sourceExpression;
134131
}
132+
133+
return null;
135134
}
136135
}
Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
using System.Linq.Expressions;
22

3-
namespace System.Linq.Dynamic.Core.Parser
3+
namespace System.Linq.Dynamic.Core.Parser;
4+
5+
/// <summary>
6+
/// Expression promoter is used to promote object or value types to their destination type when an automatic promotion is available such as: int to int?.
7+
/// </summary>
8+
public interface IExpressionPromoter
49
{
510
/// <summary>
6-
/// Expression promoter is used to promote object or value types
7-
/// to their destination type when an automatic promotion is available
8-
/// such as: int to int?
11+
/// Promote an expression.
912
/// </summary>
10-
public interface IExpressionPromoter
11-
{
12-
/// <summary>
13-
/// Promote an expression
14-
/// </summary>
15-
/// <param name="expr">Source expression</param>
16-
/// <param name="type">Destination data type to promote</param>
17-
/// <param name="exact">If the match must be exact</param>
18-
/// <param name="convertExpr">Convert expression</param>
19-
/// <returns>The promoted <see cref="Expression"/> or null.</returns>
20-
Expression? Promote(Expression expr, Type type, bool exact, bool convertExpr);
21-
}
13+
/// <param name="sourceExpression">Source expression</param>
14+
/// <param name="type">Destination data type to promote</param>
15+
/// <param name="exact">If the match must be exact</param>
16+
/// <param name="convertExpression">Convert expression</param>
17+
/// <returns>The promoted <see cref="Expression"/> or <c>null</c>.</returns>
18+
Expression? Promote(Expression sourceExpression, Type type, bool exact, bool convertExpression);
2219
}

0 commit comments

Comments
 (0)