2

I need to uniquely identify a method or constructor for any given class so that I can then invoke it at a later stage.

I had thought of using the ConstructorInfo.GetHashCode() and MethodInfo.GetHashCode() methods in the hope that the hashcode would be unique for each object inhertiting MethodBase. While they are unique they also change on each run of the program which means this method is useless to me as I need to persist the objects to database to be able to run it later (i.e. after reboots, service restarts etc).

So far, the only way I can really come up with to uniquely identify the methods and constructors is to

  1. Find a list of matching methods/constructors by name first
  2. Iterate the matching methods/constructors to see which parameter list matches the one I want.

Is there a better way of uniquely identifying a method or constructor using reflection that exists in a class without having to first iterate the matching method names and then iterating the parameter list to find the first match?

methodParams = null;
constructorInfo = null;

var methods = instanceType.GetMethods().Where(m => m.Name == constructorName);//this is required to handle methods that are overloaded
foreach (var method in methods)
{
    var internalParams = method.GetParameters();
    if (internalParams.Count() == requiredParams.Count())
    {
        var methodParamDict = internalParams.ToDictionary(x => x.Name, x => String.Empty);
        foreach (var requiredParamKey in requiredParams.Keys)
        {
            if (methodParamDict.ContainsKey(requiredParamKey))
            {
                methodParamDict[requiredParamKey] = requiredParams[requiredParamKey];
            }
        }
        if (methodParamDict.All(x => x.Value != String.Empty))
        {
            //set the methodParams to internalParams (i.e. we have found the correct overloaded method)
            methodParams = internalParams;
            constructorInfo = method as ConstructorInfo;
        }
    }
}
Community
  • 1
  • 1
Mauro
  • 4,531
  • 3
  • 30
  • 56
  • Do you have access to the code which defines the methods/ctors you need to invoke? If so, you could just decorate them with a custom attribute, for example: `[MethodName("DefaultCtor")]`, `[MethodName("CtorIntInt")]` etc. where `IntInt` means this one takes two `int`s – Balázs Jul 01 '15 at 09:09
  • For most yes, however I need to be able to run code from other namespaces and classes outside of my project. – Mauro Jul 01 '15 at 09:09
  • Please edit your question to include the actual problem you are trying to solve. – Sam Axe Jul 01 '15 at 09:16
  • @SamAxe the problem is stated in the question in that I asked if there was a better way than the way I described, however I have now explicitly restated **Is there a better way of uniquely identifying a method or constructor using reflection that exists in a class without having to first iterate the matching method names and then iterating the parameter list to find the first match?** – Mauro Jul 01 '15 at 09:20
  • That's what you are trying to do.. but not the problem you are trying to solve. – Sam Axe Jul 01 '15 at 09:21
  • My code works but I wondered if there was a better way of doing what I need to do. Not sure how I can explain it any differently. – Mauro Jul 01 '15 at 09:25
  • You need some kind of serialized method info or serialized delegate. – Stefan Steinegger Jul 01 '15 at 09:32
  • 2
    By the way, `MethodInfo` is Serializable ... it may be useful. – Stefan Steinegger Jul 01 '15 at 09:36

2 Answers2

2

Including Stefan's suggestions, you could define an extension method class like this one:

public static class CustomReflectionHelpers
{
    public static String CreateUniqueName(this MethodInfo mi)
    {
        String signatureString = String.Join(",", mi.GetParameters().Select(p => p.ParameterType.Name).ToArray());
        String returnTypeName = mi.ReturnType.Name;

        if (mi.IsGenericMethod)
        {
            String typeParamsString = String.Join(",", mi.GetGenericArguments().Select(g => g.AssemblyQualifiedName).ToArray());


            // returns a string like this: "Assembly.YourSolution.YourProject.YourClass:YourMethod(Param1TypeName,...,ParamNTypeName):ReturnTypeName
            return String.Format("{0}:{1}<{2}>({3}):{4}", mi.DeclaringType.AssemblyQualifiedName, mi.Name, typeParamsString, signatureString, returnTypeName);
        }

        return String.Format("{0}:{1}({2}):{3}", mi.DeclaringType.AssemblyQualifiedName, mi.Name, signatureString, returnTypeName);
    }
}

You can then simplify the comparison like this:

foreach (MethodInfo mi in yourType.GetMethods())
{
    if (mi.CreateUniqueName() == stringStoredInDb) { /* do something */ }
}
Balázs
  • 2,929
  • 2
  • 19
  • 34
  • The return type doesn't even count to the method signature. But the number of generic arguments would. And the class name is only unique within the same assembly. So add the assembly signature as well. – Stefan Steinegger Jul 01 '15 at 09:25
  • It indeed doesn't but it won't hurt to have. It is just a qucik idea without wanting to go into deatils. It can of course be modified to match the actual needs, for example one might not need the fully qualified type name either. – Balázs Jul 01 '15 at 09:26
  • That's true. The generic arguments of the declaring type are contained in the DeclaringType.FullName. But the generic arguments of the method itself should be added. – Stefan Steinegger Jul 01 '15 at 09:30
  • Thanks, I already store the Assembly and Class name separately, so by the time it comes to finding the methods I am already looking at the correct namespace/class. I think this will work better than serializing the whole methodinfo object as it remains humanreadable and could help with debugging at a later date. – Mauro Jul 01 '15 at 10:27
2

MethodInfo is serializable. It may be useful in your case.

See this examples.

A disadvantage of this is that you'll have problems when you update your binaries to a newer version and still want to find the method. (For such scenarios you should consider not using method names either.)

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • Thanks, this method would have been my preferred option had I not had to deal with user readability in the persisted objects. – Mauro Jul 01 '15 at 15:36