Skip to content

Commit 0a5c18c

Browse files
committed
Better type inference for generic methods
1 parent f23748d commit 0a5c18c

File tree

2 files changed

+102
-36
lines changed

2 files changed

+102
-36
lines changed

CodingSeb.ExpressionEvaluator.Tests/ExpressionEvaluatorTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2552,6 +2552,55 @@ ExpressionEvaluator evaluatorForMethodArgs()
25522552
.SetCategory("Bug resolution")
25532553
.SetCategory("MethodNameAsDelegates");
25542554

2555+
yield return new TestCaseData(new ExpressionEvaluator()
2556+
, "\"test for Upper\".ToCharArray().First(Char.IsUpper)"
2557+
, null)
2558+
.Returns('U')
2559+
.SetCategory("Bug resolution")
2560+
.SetCategory("MethodNameAsDelegates");
2561+
2562+
yield return new TestCaseData(new ExpressionEvaluator()
2563+
, "\"test for Upper\".ToCharArray().First(Char.IsUpper)"
2564+
, null)
2565+
.Returns('U')
2566+
.SetCategory("Bug resolution")
2567+
.SetCategory("MethodNameAsDelegates");
2568+
2569+
yield return new TestCaseData(new ExpressionEvaluator()
2570+
, "\"test for Upper\".ToCharArray().First(c => Char.IsUpper(c))"
2571+
, null)
2572+
.Returns('U')
2573+
.SetCategory("Bug resolution")
2574+
.SetCategory("MethodNameAsDelegates");
2575+
2576+
yield return new TestCaseData(new ExpressionEvaluator()
2577+
, "Array.Find(\"test for Upper\".ToCharArray(), Char.IsUpper)"
2578+
, null)
2579+
.Returns('U')
2580+
.SetCategory("Bug resolution")
2581+
.SetCategory("MethodNameAsDelegates");
2582+
2583+
yield return new TestCaseData(new ExpressionEvaluator()
2584+
, "Array.Find(\"test for Upper\".ToCharArray(), c => Char.IsUpper(c))"
2585+
, null)
2586+
.Returns('U')
2587+
.SetCategory("Bug resolution")
2588+
.SetCategory("MethodNameAsDelegates");
2589+
2590+
yield return new TestCaseData(new ExpressionEvaluator()
2591+
, "\"test for Upper\".ToCharArray().Any(Char.IsUpper)"
2592+
, null)
2593+
.Returns(true)
2594+
.SetCategory("Bug resolution")
2595+
.SetCategory("MethodNameAsDelegates");
2596+
2597+
yield return new TestCaseData(new ExpressionEvaluator()
2598+
, "\"test for Upper\".ToCharArray().ToList().First(Char.IsUpper)"
2599+
, null)
2600+
.Returns('U')
2601+
.SetCategory("Bug resolution")
2602+
.SetCategory("MethodNameAsDelegates");
2603+
25552604
#endregion
25562605

25572606
#endregion

CodingSeb.ExpressionEvaluator/ExpressionEvaluator.cs

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,8 +2064,8 @@ protected virtual bool EvaluateVarOrFunc(string expression, Stack<object> stack,
20642064
.ForEach(outOrRefArg => AssignVariable(outOrRefArg.VariableName, argsArray[outOrRefArg.Index + (isExtention ? 1 : 0)]));
20652065
}
20662066
else if (objType.GetProperty(varFuncName, StaticBindingFlag) is PropertyInfo staticPropertyInfo
2067-
&& (staticPropertyInfo.PropertyType.IsSubclassOf(typeof(Delegate)) || staticPropertyInfo.PropertyType == typeof(Delegate))
2068-
&& staticPropertyInfo.GetValue(obj) is Delegate del2)
2067+
&& (staticPropertyInfo.PropertyType.IsSubclassOf(typeof(Delegate)) || staticPropertyInfo.PropertyType == typeof(Delegate))
2068+
&& staticPropertyInfo.GetValue(obj) is Delegate del2)
20692069
{
20702070
stack.Push(del2.DynamicInvoke(oArgs.ToArray()));
20712071
}
@@ -3444,39 +3444,42 @@ protected virtual MethodInfo TryToCastMethodParametersToMakeItCallable(MethodInf
34443444
Type parameterType = methodInfoToCast.GetParameters()[a].ParameterType;
34453445
string paramTypeName = parameterType.Name;
34463446

3447-
if (paramTypeName.StartsWith("Predicate")
3448-
&& modifiedArgs[a] is InternalDelegate)
3447+
if (modifiedArgs[a] is InternalDelegate internalDelegate)
34493448
{
3450-
InternalDelegate led = modifiedArgs[a] as InternalDelegate;
3451-
modifiedArgs[a] = new Predicate<object>(o => (bool)led(new object[] { o }));
3452-
}
3453-
else if (paramTypeName.StartsWith("Func")
3454-
&& modifiedArgs[a] is InternalDelegate)
3455-
{
3456-
InternalDelegate led = modifiedArgs[a] as InternalDelegate;
3457-
DelegateEncaps de = new DelegateEncaps(led);
3458-
MethodInfo encapsMethod = de.GetType()
3459-
.GetMethod($"Func{parameterType.GetGenericArguments().Length - 1}")
3460-
.MakeGenericMethod(parameterType.GetGenericArguments());
3461-
modifiedArgs[a] = Delegate.CreateDelegate(parameterType, de, encapsMethod);
3462-
}
3463-
else if (paramTypeName.StartsWith("Action")
3464-
&& modifiedArgs[a] is InternalDelegate)
3465-
{
3466-
InternalDelegate led = modifiedArgs[a] as InternalDelegate;
3467-
DelegateEncaps de = new DelegateEncaps(led);
3468-
MethodInfo encapsMethod = de.GetType()
3469-
.GetMethod($"Action{parameterType.GetGenericArguments().Length}")
3470-
.MakeGenericMethod(parameterType.GetGenericArguments());
3471-
modifiedArgs[a] = Delegate.CreateDelegate(parameterType, de, encapsMethod);
3472-
}
3473-
else if (paramTypeName.StartsWith("Converter")
3474-
&& modifiedArgs[a] is InternalDelegate)
3475-
{
3476-
InternalDelegate led = modifiedArgs[a] as InternalDelegate;
3477-
modifiedArgs[a] = new Converter<object, object>(o => led(new object[] { o }));
3449+
if (paramTypeName.StartsWith("Predicate"))
3450+
{
3451+
DelegateEncaps de = new DelegateEncaps(internalDelegate);
3452+
MethodInfo encapsMethod = de.GetType()
3453+
.GetMethod("Predicate")
3454+
.MakeGenericMethod(parameterType.GetGenericArguments());
3455+
modifiedArgs[a] = Delegate.CreateDelegate(parameterType, de, encapsMethod);
3456+
}
3457+
else if (paramTypeName.StartsWith("Func"))
3458+
{
3459+
DelegateEncaps de = new DelegateEncaps(internalDelegate);
3460+
MethodInfo encapsMethod = de.GetType()
3461+
.GetMethod($"Func{parameterType.GetGenericArguments().Length - 1}")
3462+
.MakeGenericMethod(parameterType.GetGenericArguments());
3463+
modifiedArgs[a] = Delegate.CreateDelegate(parameterType, de, encapsMethod);
3464+
}
3465+
else if (paramTypeName.StartsWith("Action"))
3466+
{
3467+
DelegateEncaps de = new DelegateEncaps(internalDelegate);
3468+
MethodInfo encapsMethod = de.GetType()
3469+
.GetMethod($"Action{parameterType.GetGenericArguments().Length}")
3470+
.MakeGenericMethod(parameterType.GetGenericArguments());
3471+
modifiedArgs[a] = Delegate.CreateDelegate(parameterType, de, encapsMethod);
3472+
}
3473+
else if (paramTypeName.StartsWith("Converter"))
3474+
{
3475+
DelegateEncaps de = new DelegateEncaps(internalDelegate);
3476+
MethodInfo encapsMethod = de.GetType()
3477+
.GetMethod("Func1")
3478+
.MakeGenericMethod(parameterType.GetGenericArguments());
3479+
modifiedArgs[a] = Delegate.CreateDelegate(parameterType, de, encapsMethod);
3480+
}
34783481
}
3479-
else if(typeof(Delegate).IsAssignableFrom(parameterType)
3482+
else if (typeof(Delegate).IsAssignableFrom(parameterType)
34803483
&& modifiedArgs[a] is MethodsGroupEncaps methodsGroupEncaps)
34813484
{
34823485
MethodInfo invokeMethod = parameterType.GetMethod("Invoke");
@@ -3490,10 +3493,10 @@ protected virtual MethodInfo TryToCastMethodParametersToMakeItCallable(MethodInf
34903493
{
34913494
delegateType = Type.GetType($"System.Action`{parametersTypes.Length}");
34923495
}
3493-
else if(paramTypeName.StartsWith("Predicate"))
3496+
else if (paramTypeName.StartsWith("Predicate"))
34943497
{
34953498
delegateType = typeof(Predicate<>);
3496-
parametersTypes = parametersTypes.Concat(new Type[] { typeof(bool) }).ToArray();
3499+
methodInfoToCast = MakeConcreteMethodIfGeneric(oldMethodInfo, genericsTypes, parametersTypes);
34973500
}
34983501
else if (paramTypeName.StartsWith("Converter"))
34993502
{
@@ -3510,7 +3513,7 @@ protected virtual MethodInfo TryToCastMethodParametersToMakeItCallable(MethodInf
35103513

35113514
modifiedArgs[a] = Delegate.CreateDelegate(delegateType, methodsGroupEncaps.ContainerObject, methodForDelegate);
35123515

3513-
if(oldMethodInfo.IsGenericMethod &&
3516+
if (oldMethodInfo.IsGenericMethod &&
35143517
methodInfoToCast.GetGenericArguments().Length == parametersTypes.Length &&
35153518
!methodInfoToCast.GetGenericArguments().SequenceEqual(parametersTypes) &&
35163519
string.IsNullOrWhiteSpace(genericsTypes))
@@ -3543,6 +3546,15 @@ protected virtual MethodInfo TryToCastMethodParametersToMakeItCallable(MethodInf
35433546
if (!parameterType.GetElementType().IsAssignableFrom(modifiedArgs[a].GetType()))
35443547
modifiedArgs[a] = Convert.ChangeType(modifiedArgs[a], parameterType.GetElementType());
35453548
}
3549+
else if (modifiedArgs[a].GetType().IsArray
3550+
&& typeof(IEnumerable).IsAssignableFrom(parameterType)
3551+
&& oldMethodInfo.IsGenericMethod
3552+
&& string.IsNullOrWhiteSpace(genericsTypes)
3553+
&& methodInfoToCast.GetGenericArguments().Length == 1
3554+
&& !methodInfoToCast.GetGenericArguments()[0].Equals(modifiedArgs[a].GetType().GetElementType()))
3555+
{
3556+
methodInfoToCast = MakeConcreteMethodIfGeneric(oldMethodInfo, genericsTypes, new Type[] { modifiedArgs[a].GetType().GetElementType() });
3557+
}
35463558
else
35473559
{
35483560
modifiedArgs[a] = Convert.ChangeType(modifiedArgs[a], parameterType);
@@ -4029,6 +4041,11 @@ public void Action0()
40294041
lambda();
40304042
}
40314043

4044+
public bool Predicate<T1>(T1 arg1)
4045+
{
4046+
return (bool)lambda(arg1);
4047+
}
4048+
40324049
public void Action1<T1>(T1 arg1)
40334050
{
40344051
lambda(arg1);

0 commit comments

Comments
 (0)