Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert from one delegate to another. Pseudo cast

Tags:

c#

.net

delegates

We are using IoC and have our logging exposed with it. We are using Common.Logging and I have written a matching delegate for Common.Logging.FormatMessageHandler but I do not know how to convert from our version of that delegate to the one that the Common.Logging api is expecting.

This question appears to be similar but I do not understand how to convert from my implemented type to the known type that I want to call. Dynamically casting one type of delegate to another

Here is my delegate signature:

public delegate string FormatMessageHandler(string format, params object[] args)

Here is Common.Logging's:

public delegate string FormatMessageHandler(string format, params object[] args)

Same name (not that is matters) and same number of parameters. Both are known at compile time so it should be something obvious but I am not seeing it.

like image 622
David W Avatar asked Jul 05 '11 21:07

David W


2 Answers

Manually you can do this, but it is as expensive as the reflection involved at conversion. Once the delegate is converted it behaves basically the same...

internal class Program
{
    //An example delegate target
    static void Click(object o, EventArgs e) { }

    //A simple test method
    static void Main(string[] args)
    {
        EventHandler onclick = Click;
        EventHandler<EventArgs> converted;
        if (!TryConvertDelegate(onclick, out converted))
            throw new Exception("failed");
    }

    //The conversion of one delegate type to another
    static bool TryConvertDelegate<TOldType, TNewType>(TOldType oldDelegate, out TNewType newDelegate)
        where TOldType : class, System.ICloneable, System.Runtime.Serialization.ISerializable
        where TNewType : class, System.ICloneable, System.Runtime.Serialization.ISerializable
    {
        if (!typeof(Delegate).IsAssignableFrom(typeof(TOldType)) || !typeof(Delegate).IsAssignableFrom(typeof(TNewType)))
            throw new ArgumentException(); //one of the types is not a delegate

        newDelegate = default(TNewType);
        Delegate handler = oldDelegate as System.Delegate;
        if (handler == null)
            return true; //null in, null out

        Delegate result = null;
        foreach (Delegate d in handler.GetInvocationList())
        {
            object copy = System.Delegate.CreateDelegate(typeof(TNewType), d.Target, d.Method, false);
            if (copy == null)
                return false; // one or more can not be converted
            result = System.Delegate.Combine(result, (System.Delegate)copy);
        }
        newDelegate = result as TNewType;
        return (newDelegate != null);
    }
like image 145
csharptest.net Avatar answered Nov 15 '22 02:11

csharptest.net


Why are you not using Common.Logging's delegate in the first place if it is exactly the same?

However, a solution to your problem is to either use the dynamic cast explained in the article linked in the question you mentioned, or you do it like this:

YourNamespace.FormatMessageHandler yourHandler = ...;
Common.Logging.FormatMessageHandler handler = (f, a) => yourHandler(f, a);

UPDATE:
According to your comment, you want something like that:

public void Error(Action<Your.FormatMessageHandler> formatMessageCallback)
{
    _logger.Error(h => formatMessageCallback((f, a) => h(f, a)));
}

This will create a new action with one parameter h of type Common.Logging.FormatMessageHandler which calls the supplied action formatMessageCallback with a new delegate of Your.FormatMessageHandler that accepts two parameters f and a. This new delegate in turn calls h with the two supplied parameters.

like image 39
Daniel Hilgarth Avatar answered Nov 15 '22 04:11

Daniel Hilgarth