Ambiguous match found when using reflection to find Generic method
Asked Answered
G

4

10

I'm using reflection to find the generic method for Newtonsoft JsonConvert.DeserializedObject<T> but am finding that it's returning an ambiguous match for the non-generic version JsonConvert.DeserializeObject, here is the code which tries to get the generic method:

return typeof(JsonConvert).GetMethod("DeserializeObject", new Type[] { typeof(string) }).MakeGenericMethod(genericType);

I've specified that I want the method which takes a string as its only argument but both the generic and non-generic version have matching parameter lists, and I receive the ambiguous match error.

Is it possible to to get the generic version using GetMethod in this way? I know I can use Linq and GetMethods() to find it e.g:

var method = typeof(JsonConvert).GetMethods().FirstOrDefault(
            x => x.Name.Equals("DeserializeObject", StringComparison.OrdinalIgnoreCase) &&
            x.IsGenericMethod && x.GetParameters().Length == 1 &&
            x.GetParameters()[0].ParameterType == typeof(string));

But this is a bit cumbersome, there must be a better way.

Genus answered 24/10, 2018 at 9:23 Comment(4)
@DavidG - Hmm.. isn't the generic type passed in here MakeGenericMethod(genericType); ?Genus
Sorry, misspoke in that comment. What is actually happening though is GetMethod cannot choose between JsonConvert.DeserializeObject(string) and JsonConvert.DeserializeObject<T>(string). I think you are stuck with the Linq method here.Atingle
@Atingle - Yeah i was hoping there was some way to specify I want the generic version in GetMethod() but I guess not, thanks anyway!Genus
Does this answer your question? Reflection GetMethod with a type paramenterGaiser
E
3

You can derive from Binder class

class MyBinder : Binder
{
    public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
    {          
        return match.First(m => m.IsGenericMethod);
    }

    #region not implemented
    public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state) => throw new NotImplementedException();
    public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture) => throw new NotImplementedException();
    public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers) => throw new NotImplementedException();
    public override object ChangeType(object value, Type type, CultureInfo culture) => throw new NotImplementedException();
    public override void ReorderArgumentArray(ref object[] args, object state) => throw new NotImplementedException();
    #endregion
}

Usage:

var method = typeof(JsonConvert).GetMethod("DeserializeObject",
    BindingFlags.Public | BindingFlags.Static,
    new MyBinder(),
    new[] {typeof(string)},
    null);

In your case MyBinder will receive two candidates in SelectMethod

public static object DeserializeObject(string value)
public static T DeserializeObject<T>(string value)

Code above will select first generic method

Epner answered 24/10, 2018 at 9:44 Comment(2)
Really useful! though probably overkill in my case, but will accept as the answer :)Genus
Nice, though this is basically pretty similar to the original Linq, nonetheless useful to know.Atingle
B
5

I think you want this:

var method = typeof(JsonConvert).GetMethods().FirstOrDefault(
    x => x.Name.Equals("DeserializeObject", StringComparison.OrdinalIgnoreCase) &&
    x.IsGenericMethod && x.GetParameters().Length == 1)
    ?.MakeGenericMethod(genericType);
Bombardon answered 24/10, 2018 at 9:27 Comment(5)
This is exactly what I have in the OP - minus the param type check, I'm looking for a way to do it with GetMethod()Genus
I don´t see exactly this one (4th line is different)Bombardon
Ok, it's a subtle tweak but my point is, it's the same solution I already have in the OP which works, but id like to do it with GetMethod() if at all possible.Genus
@Genus there is no way using GetMethod, you have to use GetMethods and filter afterwards.Paleontology
updated with genericType so this one will give you JsonConvert.DeserializeObject<genericType>(string)Bombardon
V
5

There are GetMethod methods which do distinguish between generic and non-generic methods; an example is this one with the integer parameter genericParamCount:

Searches for the specified public method whose parameters match the specified generic parameter count and argument types.

The following code:

var genericMethod = typeof(JsonConvert)
    .GetMethod("DeserializeObject", 1, new Type[] { typeof(string) })
    .MakeGenericMethod(typeof(string));
var otherMethod = typeof(JsonConvert)
    .GetMethod("DeserializeObject", 0, new Type[] { typeof(string) });
Console.WriteLine(genericMethod);
Console.WriteLine(otherMethod);

will print

System.String DeserializeObject[String](System.String)
System.Object DeserializeObject(System.String)

so this is perhaps the shortest solution to your problem.

Vagrancy answered 18/6, 2021 at 15:0 Comment(0)
E
3

You can derive from Binder class

class MyBinder : Binder
{
    public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
    {          
        return match.First(m => m.IsGenericMethod);
    }

    #region not implemented
    public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state) => throw new NotImplementedException();
    public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture) => throw new NotImplementedException();
    public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers) => throw new NotImplementedException();
    public override object ChangeType(object value, Type type, CultureInfo culture) => throw new NotImplementedException();
    public override void ReorderArgumentArray(ref object[] args, object state) => throw new NotImplementedException();
    #endregion
}

Usage:

var method = typeof(JsonConvert).GetMethod("DeserializeObject",
    BindingFlags.Public | BindingFlags.Static,
    new MyBinder(),
    new[] {typeof(string)},
    null);

In your case MyBinder will receive two candidates in SelectMethod

public static object DeserializeObject(string value)
public static T DeserializeObject<T>(string value)

Code above will select first generic method

Epner answered 24/10, 2018 at 9:44 Comment(2)
Really useful! though probably overkill in my case, but will accept as the answer :)Genus
Nice, though this is basically pretty similar to the original Linq, nonetheless useful to know.Atingle
A
2

The problem is that Type.GetMethod doesn't allow you to specify the generic type meaning that this code:

var method = typeof(JsonConvert).GetMethod("DeserializeObject", new[] { typeof(string)})

is trying to resolve between the 2 matching methods JsonConvert.DeserializeObject(string) and JsonConvert.DeserializeObject<T>(string).

Unfortunately you are stuck with the Linq option to do this.

Atingle answered 24/10, 2018 at 9:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.