forked from mdsol/FastAndFaster
/
TypeHelper.cs
132 lines (121 loc) · 4.78 KB
/
TypeHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace FastAndFaster.Helpers
{
internal static class TypeHelper
{
internal static Type GetTypeByName(string typeName)
{
var type = Type.GetType(typeName);
if (type is null)
{
throw new ArgumentException(
$"Cannot find any class with Assembly Qualified Name: {typeName}");
}
return type;
}
internal static ConstructorInfo GetConstructorInfoByType(Type type, Type[] parameterTypes)
{
var constructorInfo = type.GetConstructor(parameterTypes);
if (constructorInfo is null)
{
throw new ArgumentException(
$"Cannot find constructor with given signature for class: {type.AssemblyQualifiedName}");
}
return constructorInfo;
}
internal static MethodInfo GetMethodInfoByName(
Type type, string methodName, Type[] parameterTypes, GenericInfo genericInfo)
{
MethodInfo methodInfo = null;
if (genericInfo is null)
{
methodInfo = type.GetMethod(methodName, parameterTypes);
}
else
{
var candidates = type.GetMethods().Where(n => n.Name == methodName && n.IsGenericMethod);
methodInfo = FilterMethodByParameterTypes(candidates, parameterTypes, genericInfo);
methodInfo = methodInfo?.MakeGenericMethod(genericInfo.GenericType);
}
if (methodInfo is null)
{
throw new ArgumentException($"Cannot find method [{methodName}] with the given signature");
}
return methodInfo;
}
/// <summary>
/// Concatenate the names of all parameter types into a string,
/// then use the hash code of that string as part of the method's identity.
/// We cannot use the hashcode of "Type[] types" directly because
/// 2 different Type arrays with the exact content still hash to 2 different values.
/// </summary>
/// <param name="types"></param>
/// <returns></returns>
internal static int GetParameterTypesIdentity(Type[] types, GenericInfo genericInfo = null)
{
var sb = new StringBuilder();
foreach (var type in types)
{
sb.Append(type.AssemblyQualifiedName);
}
if (genericInfo is object)
{
sb.Append("_GenericTypeIndex_");
foreach (var index in genericInfo.GenericTypeIndex)
{
sb.Append(index);
sb.Append(",");
}
sb.Append("_GenericType_");
foreach (var type in genericInfo.GenericType)
{
sb.Append(type.AssemblyQualifiedName);
}
}
var concatenateName = sb.ToString();
return concatenateName.GetHashCode();
}
private static MethodInfo FilterMethodByParameterTypes(
IEnumerable<MethodInfo> methods, Type[] parameterTypes, GenericInfo genericInfo)
{
foreach (var method in methods)
{
if (method.GetGenericArguments().Length != genericInfo.GenericType.Length)
{
continue;
}
var parameters = method.GetParameters();
if (parameters.Length != parameterTypes.Length)
{
continue;
}
if (IsTypesMatch(parameters, parameterTypes, genericInfo.GenericTypeIndex))
{
return method;
}
}
return null;
}
private static bool IsTypesMatch(ParameterInfo[] parameters, Type[] parameterTypes, int[] genericTypeIndex)
{
for (var i = 0; i < parameters.Length; i++)
{
var genericParamNotExistInIndex =
parameters[i].ParameterType.IsGenericParameter && !genericTypeIndex.Contains(i);
var nonGenericParamExistsInIndex =
!parameters[i].ParameterType.IsGenericParameter && genericTypeIndex.Contains(i);
var typeOfNoneGenericParamNotMatch =
!parameters[i].ParameterType.IsGenericParameter && parameters[i].ParameterType != parameterTypes[i];
if (genericParamNotExistInIndex || nonGenericParamExistsInIndex || typeOfNoneGenericParamNotMatch)
{
return false;
}
}
return true;
}
}
}