AmbiguousMatchException if record type has more than one (inherited) Deconstruct method
thoemmi opened this issue · comments
I git a AmbiguousMatchException when calling MiniValidator.TryValidate
for my object. I took me some time to strip it down to a minimal repo.
// code
var thingToValidate = new DerivedType(new Dictionary<string, string>());
var isValid = MiniValidator.TryValidate(thingToValidate, out var errors);
// types
public abstract record BaseType(string Type);
public record DerivedType(Dictionary<string, string> Fields) : BaseType("derived");
The error is
System.Reflection.AmbiguousMatchException
Ambiguous match found.
at System.RuntimeType.GetMethodImplCommon(String name, Int32 genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConv, Type[] types, ParameterModifier[] modifiers)
at System.Type.GetMethod(String name, BindingFlags bindingAttr)
at MiniValidation.TypeDetailsCache.Visit(Type type, HashSet`1 visited, Boolean& requiresAsync) in C:\Users\Thomas\source\repos\MiniValidation\src\MiniValidation\TypeDetailsCache.cs:line 56
at MiniValidation.TypeDetailsCache.Visit(Type type) in C:\Users\Thomas\source\repos\MiniValidation\src\MiniValidation\TypeDetailsCache.cs:line 34
at MiniValidation.TypeDetailsCache.Get(Type type) in C:\Users\Thomas\source\repos\MiniValidation\src\MiniValidation\TypeDetailsCache.cs:line 24
at MiniValidation.MiniValidator.RequiresValidation(Type targetType, Boolean recurse) in C:\Users\Thomas\source\repos\MiniValidation\src\MiniValidation\MiniValidator.cs:line 44
at MiniValidation.MiniValidator.TryValidate[TTarget](TTarget target, Boolean recurse, Boolean allowAsync, IDictionary`2& errors) in C:\Users\Thomas\source\repos\MiniValidation\src\MiniValidation\MiniValidator.cs:line 104
at MiniValidation.MiniValidator.TryValidate[TTarget](TTarget target, IDictionary`2& errors) in C:\Users\Thomas\source\repos\MiniValidation\src\MiniValidation\MiniValidator.cs:line 64
at MiniValidation.UnitTests.TryValidate.MyTest() in C:\Users\Thomas\source\repos\MiniValidation\tests\MiniValidation.UnitTests\TryValidate.cs:line 332
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
The exception is thrown in TypeDetailsCache.cs, line 56:
if (type.GetMethod("Deconstruct") is { } deconstruct)
This means there is more than one method named Deconstruct
. I've put a breakpoint in that line and called GetMethods("Deconstruct")
in the "Immediate Window":
type.GetMethods().Where(m => m.Name == "Deconstruct")
{System.Linq.Enumerable.WhereArrayIterator<System.Reflection.MethodInfo>}
[0]: {Void Deconstruct(System.Collections.Generic.Dictionary`2[System.String,System.String] ByRef)}
[1]: {Void Deconstruct(System.String ByRef)}
Apparently there are two Deconstruct
methods, one from the base record, and one from the derived record.
Maybe adding BindingFlags.DeclaredOnly
as a second parameter to GetMethod
might help, but I did not fully understand the implications yet.