Search code examples
c#delegatessystem.reflectionpropertyinfomethodinfo

Assign method to private delegate without breaking the current assignment


I'd like to use private delegate from some class, but without breaking the current assignment. Can I somehow ensure that my changes (done by reflection) do not disconnect WriteToDebug?

Delegate is private and "Inform" is private

public class ExternalClass
{
    private delegate void InformDelegate(string info);//PRIVATE!
    private InformDelegate Inform { get; set; }//PRIVATE!

    public ExternalClass()
    {
        Inform = WriteToDebug;//Default method
    }

    public void Foo(string bar)
    {
        Inform?.Invoke(bar);
    }

    private void WriteToDebug(string info)
    {
        Debug.WriteLine($"Info: {info}");
    }
}

Adding my method to the delegate and some test (Debug.WriteLine + MessageBox.Show are required)

public class MyClass
{
    public void Test(string message)
    {
        ExternalClass externalClass = new ExternalClass();
        externalClass.Foo(message);

        //Assign MyMethod to SomeClass.Inform            
        MethodInfo mi = GetType().GetMethod(nameof(MyMethod), BindingFlags.NonPublic | BindingFlags.Instance);
        PropertyInfo pi = externalClass.GetType().GetProperty("Inform", BindingFlags.NonPublic | BindingFlags.Instance);
        object del = Delegate.CreateDelegate(pi.PropertyType, this, mi);
        Type type = externalClass.GetType().GetNestedType("Inform", BindingFlags.Public | BindingFlags.NonPublic);
        pi.SetValue(externalClass, del);

        //Try Foo again
        externalClass.Foo(message);//TODO: Ensure that the call "Inform? .Invoke (bar);" triggered both methods: WriteToDebug and MyMethod.
    }

    private void MyMethod(string msg)
    {
        MessageBox.Show(msg);
    }
}

Verification

internal class Program
{
    private static void Main(string[] args)
    {
        MyClass myClass = new MyClass();
        myClass.Test("Hello Word!");

        Console.ReadKey();
    }
}

I will be grateful for your help


Solution

  • You have to combine your delegate with the existing one (here: pointing to WriteToDebug) via Delegate.Combine.

    Retrieve this already existing one via:

    Delegate original = pi.GetValue(externalClass) as Delegate;
    

    and combine your new one via:

    Delegate combined = Delegate.Combine(original, del);
    

    Note that the order of the passed in delegates represents the order of execution.

    Your full code from above will look like below.

    ExternalClass externalClass = new ExternalClass();
    
    MethodInfo mi = GetType().GetMethod(nameof(MyMethod), BindingFlags.NonPublic | BindingFlags.Instance);
    PropertyInfo pi = externalClass.GetType().GetProperty("Inform", BindingFlags.NonPublic | BindingFlags.Instance);
    
    Delegate del = Delegate.CreateDelegate(pi.PropertyType, this, mi);
    
    Delegate original = pi.GetValue(externalClass) as Delegate;
    Delegate combined = Delegate.Combine(original, del);
    
    pi.SetValue(externalClass, combined);
    
    externalClass.Foo(message);