How to create a delegate to an instance method with a null target?
Asked Answered
K

3

8

I've noticed that the Delegate class has a Target property, that (presumably) returns the instance the delegate method will execute on. I want to do something like this:

void PossiblyExecuteDelegate(Action<int> method)
{
    if (method.Target == null)   
    {
        // delegate instance target is null
        // do something
    }
    else
    {
         method(10);
         // do something else
    }
}

When calling it, I want to do something like:

class A
{
    void Method(int a) {}

    static void Main(string[] args)
    {
        A a = null;
        Action<int> action = a.Method;
        PossiblyExecuteDelegate(action);
    }
}

But I get an ArgumentException (Delegate to an instance method cannot have a null 'this') when I try to construct the delegate. Is what I want to do possible, and how can I do it?

Karenkarena answered 4/6, 2009 at 16:23 Comment(1)
Why do you want to do this? You have instances that are null?Gestapo
K
17

Ahah! found it!

You can create an open instance delegate using a CreateDelegate overload, using a delegate with the implicit 'this' first argument explicitly specified:

delegate void OpenInstanceDelegate(A instance, int a);

class A
{
    public void Method(int a) {}

    static void Main(string[] args)
    {
        A a = null;
        MethodInfo method = typeof(A).GetMethod("Method");
        OpenInstanceDelegate action = (OpenInstanceDelegate)Delegate.CreateDelegate(typeof(OpenInstanceDelegate), a, method);

        PossiblyExecuteDelegate(action);
    }
}
Karenkarena answered 4/6, 2009 at 16:45 Comment(1)
Yes, as I edited my own response, this is possible with reflection, but not with standard compiled .NET code. Is your concern that people will be passing you reflectively declared delegates?Rightwards
R
1

In order to do this you would have to pass a static method to PossiblyExecuteDelegate(). This will give you a null Target.

class A
{
    void Method(int a) {}
    static void Method2(int a) {}

    static void Main(string[] args)
    {
        PossiblyExecuteDelegate(A.Method2);

        A a = new A();

        PossiblyExecuteDelegate(a.Method);
    }
}

Edit: It is possible to pass a delegate to an instance method with no target via reflection, but not using standard compiled code.

Rightwards answered 4/6, 2009 at 16:28 Comment(3)
I mean test whether the instance method would be called on a null instance, if it was called. The problem is getting the instance in the first place. I do remember reading something about open delegates, but I can't find hide nor hair of it now...Karenkarena
Just found the docs again - you can create an open instance delegate using a Delegate.CreateDelegate overload (posted an answer with details)Karenkarena
@thecoop: Yes, I should have noted that this would be possible with reflection, but not with any compile-time-safe code. Answer edited.Rightwards
J
0

It is possible with Delegate.CreateDelegate, exactly with the overload with the signature: CreateDelegate (Type, object, MethodInfo)

If you specify "null" for the second parameter (target) then you have to put an extra parameter into the delegate type, that specifies the instance type, and when you invoke the delegate, the instance has to be passed as first argument, followed by the "real" parameters of the method.

class Test
{
    public int AddStrings(string a, string b)
    {
        return int.Parse(a) + int.Parse(b);
    }

    static void Main()
    {
        var test = new Test();
        var methodInfo = test.GetType().GetMethod("AddStrings");
        // note the first extra parameter of the Func, is the owner type
        var delegateType = typeof(Func<Test, string, string, int>);
        var del = Delegate.CreateDelegate(delegateType, null, methodInfo);

        var result = (int)del.DynamicInvoke(test, "39", "3");
    }
}
Jiujitsu answered 27/10, 2017 at 16:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.