开发者

Is it possible to pass an arbitrary method group as a parameter to a method?

I'd like write a function like the following

// The type 'MethodGroup' below doesn't exist.  This is fantasy-code.
public void MyFunction(MethodGroup g)
{
    // do something with the method group
}

The later, I could call MyFunction with any method group. Something like this.

MyFunction(Object.Equals)

If I commit to a signature, then things work fine.

public void MyFunction(Func<object, object, bool> f)
{
    // do something with known delegate
}
...
MyFunction(Object.Equals)

The method group Object.Equals is happily coerced into the known delegate type Func<object, object, bool>, but I don't want to commit to a particular signature. I'd like to pass any method group to MyFunction.

Method groups cannot be converted to System.Object

public void MyFunction(object o)
{
    // do something with o
}
...
MyFunction(Object.Equals) // doesn't work

I think that everyone's forgotten braces on a method call and discovered this at some point. I'm hoping that this doesn't mean that method groups aren't (or can't be converted) to first class objects.

I don't think that Linq expressions will give the kind of generality I'm looking for, but I could certainly be missing something.

I should also mention that it would be fine if the method group contained overloads, provided I have a way of inspecting the method group.

What would I do with a method group? I could print all the signatures of all the the methods in the group (overloads, extension methods etc), or I could 'invoke' the group with some arguments (have it resolve to the correct overload in the group if possible). There are other ways to do these things but they are some things you might want to do with a method group.

As several people have mentioned, I can accept a Delegate, and cast to a particular known delegate type when I call MyFunction.

public void MyFunction(Delegate d)
{
    // do something with d
}
...
MyFunction((Func<object, object, bool>)Obj开发者_如何学Cect.Equals)

But this isn't quite the same as passing the entire method group. This selects one method from the group and converts it to a particular delegate. I would really like to pass the whole group in one shot.


I think an important question here is: what would you do with a method group if you could ever be passed one?

Keep in mind, a method group is just a name for a set of methods - there may be one or more - and they may be overloads or extension methods. Method groups are not a concept that is preserved in compiled code - the compiler supports conversions from method groups to delegates - but in order for this to be possible the compiler must be able to resolve without any ambiguity exactly what method you intend to pass.

Method groups are only valid as parameters to functions if the function clearly defines the signature of the method. So, for example: public void MyFunction( Func<int> f ) can be passed a method group.

The closest you can come is to write a function that accepts the type Delegate:

public void MyFunction( Delegate d ) { ... }

but you will still not be able to pass method groups in because there is no conversion from method group to Delegate. You will have to cast to a particular delegate signature:

// call MyFunction with a particular delegate
MyFunction( (Func<string>)myObj.ToString );  // method group conversion may occur


You can pass a delegate :

public void MyFunction(Delegate d)
{
    // do something with the method group
}

However you will have to specify the delegate type when calling the method :

MyFunction(new Func<object, object, bool>(Object.Equals));

Note : the code above is not passing a method group but a single method... I don't think it is possible to pass a method group. Why would you want to do that ?


The asker wants to do this because he wants meta data about the method group that is the same for any method in the group (such as name) or he wants to do something with all of the methods in the group. I believe nameof (C# Reference) is what would prove most useful here.

I define MyFunction as he initially expected, but it is important to note that whatever this does is only applying to one method signature. This may not actually need to be called if MethodInfo gives everything you need about the function.

public void MyFunction(Delegate d)
{
    // do something with d
}

Then we will provide a wrapper that takes a methodName, type, and optionally a calling object. The calling object is not required to generate the delegate even if it is an instance method, but actually executing the delegate may fail if it is null. Providing a calling object for a static method will cause binding errors though even for creating the delegate. I have written the method below to return both static and instance methods and bind any instance methods to the caller whereas static methods bind to null. You could modify it to skip static methods when the caller is an instance and vice-versa.

public void MyFunctionMethodGroup(string methodName, Type type, object caller)
{
    var methods = type.GetMethods().Where(x => x.Name == methodName);
    foreach(var method in methods)
    {
        Delegate myDelegate = method.CreateDelegate(Expression.GetDelegateType(
            method.GetParameters().Select(x => x.ParameterType)
            .Concat(new[] { method.ReturnType })
            .ToArray()), method.IsStatic ? null : caller);
        MyFunction(myDelegate);
    }
}

Here are a few calling examples.

Static method

MyFunctionMethodGroup(nameof(String.IsNullOrEmpty), typeof(String), null);

Instance method

Notice that you can call the function with from either the class name even if it is an instance function. This goes well if you do not need to invoke the method and don't have a caller. You can also invoke it from an instance without needing to explicitly write the class name.

string xyz = "2";
MyFunctionMethodGroup(nameof(String.Contains), typeof(String), xyz);
MyFunctionMethodGroup(nameof(xyz.Contains), xyz.GetType(), xyz);

Advanced Cases

Extension Methods

It is worth remembering that extension methods are called as static methods on the namespace in which they are defined. If you needed the instance parameter for what you wanted to do in MyFunction, then it would have to be passed in as a new parameter separate from the caller which would be null as extension methods are static.

Generic Methods

Additionally, the code above does not work properly with generic methods. Here is an example of apply the generic type of the method.

   public void MyFunctionMethodGroup(string methodName, Type type, object caller = null, params Type[] genericParameters)
    {
        //GetMethods only returns public methods. If you need protected/private, modify as appropriate.
        var methods = type.GetMethods().Where(x => x.Name == methodName);
        foreach (var method1 in methods)
        {
            MethodInfo method = method1.IsGenericMethod ? method1.MakeGenericMethod(genericParameters) : method1;
            Type delegateType = Expression.GetDelegateType(
                method.GetParameters().Select(x => x.ParameterType)
                .Concat(new[] { method.ReturnType })
                .ToArray());
            Delegate myDelegate = method.CreateDelegate(delegateType, method.IsStatic ? null : caller);
            MyFunction(myDelegate);
        }
    }

Generic Classes

The method above may not fully support methods in generic classes if the class is passed in without the generic types defined in the Type parameter.

Output/Reference Parameters

The code as written does not support output or reference parameters. It may not support ref parameters either.


I think you could use signature like this:

MyFunc<T>(EventHandler<T> newDelegate)
{
    object storedDelegate = newDelegate;
    // after that you can use storedDelegate later
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜