Skip to content

Commit 42f4d2b

Browse files
committed
Removed collision risk in builder implementation
1 parent f8aad64 commit 42f4d2b

File tree

1 file changed

+57
-26
lines changed

1 file changed

+57
-26
lines changed

Refit/RequestBuilderImplementation.cs

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ partial class RequestBuilderImplementation : IRequestBuilder
3030
HttpMethod.Head
3131
};
3232
readonly Dictionary<string, List<RestMethodInfo>> interfaceHttpMethods;
33-
readonly ConcurrentDictionary<int, Func<HttpClient, object[], object>> restResultFuncForMethodsMap;
33+
readonly ConcurrentDictionary<RestMethodKey, Func<HttpClient, object[], object>> restResultFuncForMethodsMap;
3434
readonly IContentSerializer serializer;
3535
readonly RefitSettings settings;
3636
public Type TargetType { get; }
@@ -41,7 +41,7 @@ public RequestBuilderImplementation(Type refitInterfaceType, RefitSettings refit
4141

4242
settings = refitSettings ?? new RefitSettings();
4343
serializer = settings.ContentSerializer;
44-
restResultFuncForMethodsMap = new ConcurrentDictionary<int, Func<HttpClient, object[], object>>();
44+
restResultFuncForMethodsMap = new ConcurrentDictionary<RestMethodKey, Func<HttpClient, object[], object>>();
4545

4646
if (refitInterfaceType == null || !refitInterfaceType.GetTypeInfo().IsInterface)
4747
{
@@ -139,29 +139,7 @@ RestMethodInfo CloseGenericMethodIfNeeded(RestMethodInfo restMethodInfo, Type[]
139139

140140
public Func<HttpClient, object[], object> BuildRestResultFuncForMethod(string methodName, Type[] parameterTypes = null, Type[] genericArgumentTypes = null)
141141
{
142-
/* Build a unique key for this specific rest method, by combining
143-
* all the necessary info: method name, parameter types and generic
144-
* arguments. We're doing this work here since we can't just modify
145-
* the existing interface, as that would be a breaking chance.*/
146-
HashCode hashCode = default;
147-
148-
hashCode.Add(methodName);
149-
150-
hashCode.Add(parameterTypes);
151-
if (!(parameterTypes is null))
152-
{
153-
foreach (var type in parameterTypes)
154-
hashCode.Add(type);
155-
}
156-
157-
hashCode.Add(genericArgumentTypes);
158-
if (!(genericArgumentTypes is null))
159-
{
160-
foreach (var type in genericArgumentTypes)
161-
hashCode.Add(type);
162-
}
163-
164-
var key = hashCode.ToHashCode();
142+
var key = new RestMethodKey(methodName, parameterTypes, genericArgumentTypes);
165143

166144
/* Fast path if we have already generated this specific method before: in that
167145
* case we simply get it from the cache and return it immediately.
@@ -175,8 +153,61 @@ public Func<HttpClient, object[], object> BuildRestResultFuncForMethod(string me
175153
return BuildAndAddRestResultFuncForMethod(key, methodName, parameterTypes, genericArgumentTypes);
176154
}
177155

156+
/// <summary>
157+
/// A <see langword="struct"/> acting as key for a given generated REST method.
158+
/// </summary>
159+
private readonly struct RestMethodKey : IEquatable<RestMethodKey>
160+
{
161+
private readonly string methodName;
162+
private readonly Type[] parameterTypes;
163+
private readonly Type[] genericArgumentTypes;
164+
165+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
166+
internal RestMethodKey(string methodName, Type[] parameterTypes, Type[] genericArgumentTypes)
167+
{
168+
this.methodName = methodName;
169+
this.parameterTypes = parameterTypes;
170+
this.genericArgumentTypes = genericArgumentTypes;
171+
}
172+
173+
/// <inheritdoc/>
174+
public override bool Equals(object obj)
175+
{
176+
if (obj is null) return false;
177+
if (obj.GetType() != typeof(RestMethodKey)) return false;
178+
179+
return Equals((RestMethodKey)obj);
180+
}
181+
182+
/// <inheritdoc/>
183+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
184+
public bool Equals(RestMethodKey other)
185+
{
186+
return
187+
methodName == other.methodName &&
188+
parameterTypes == other.parameterTypes &&
189+
genericArgumentTypes == other.genericArgumentTypes;
190+
}
191+
192+
/// <inheritdoc/>
193+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
194+
public override int GetHashCode()
195+
{
196+
HashCode hashCode = default;
197+
198+
/* We can just compare the arrays directly since the
199+
* stubs are caching all the array instances, so we
200+
* don't need to iterate and inspect each array value. */
201+
hashCode.Add(methodName);
202+
hashCode.Add(parameterTypes);
203+
hashCode.Add(genericArgumentTypes);
204+
205+
return hashCode.ToHashCode();
206+
}
207+
}
208+
178209
[MethodImpl(MethodImplOptions.NoInlining)]
179-
private Func<HttpClient, object[], object> BuildAndAddRestResultFuncForMethod(int key, string methodName, Type[] parameterTypes = null, Type[] genericArgumentTypes = null)
210+
private Func<HttpClient, object[], object> BuildAndAddRestResultFuncForMethod(RestMethodKey key, string methodName, Type[] parameterTypes = null, Type[] genericArgumentTypes = null)
180211
{
181212
return restResultFuncForMethodsMap.GetOrAdd(key, _ =>
182213
{

0 commit comments

Comments
 (0)