56

I have code:

public delegate int SomeDelegate(int p);

public static int Inc(int p) {
    return p + 1;
}

I can cast Inc to SomeDelegate or Func<int, int>:

SomeDelegate a = Inc;
Func<int, int> b = Inc;

but I can't cast Inc to SomeDelegate and after that cast to Func<int, int> with usual way like this:

Func<int, int> c = (Func<int, int>)a; // Сompilation error

How I can do it?

AndreyAkinshin
  • 18,603
  • 29
  • 96
  • 155

8 Answers8

81

There's a much simpler way to do it, which all the other answers have missed:

Func<int, int> c = a.Invoke; 

See this blog post for more info.

Winston Smith
  • 21,585
  • 10
  • 60
  • 75
  • 3
    Nice. It's similar to Gamlor's solution, but without the anonymous method. Still, it's wrapping the original delegate, unlike my proposed solution. – Diego Mijelshon Dec 15 '09 at 15:43
  • 7
    If anyone's not sure what Diego means, take a look at the Target and Method properties of the original 'a' delegate, and the 'c' delegate. With Diego's mechanism, 'c' points directly to the original method just like 'a' does. With Winston's method, it does not - it points to the delegate which in turn points to the original method, so you get an unnecessary extra level of indirection. – Ian Griffiths Feb 15 '12 at 09:27
  • 2
    As Diego and Ian mentioned, this solution wraps the original the original delegate. Therefore, if you are using this solution with events, you will not be able to unsubscribe. With Diego's solution you can. – Verax May 01 '12 at 02:18
  • 2
    @Verax @IanGriffiths: I did a quick experiment (net45) and `a.Invoke` seems to unsubscribe ok? https://gist.github.com/dtchepak/7799703 – David Tchepak Dec 05 '13 at 03:33
56
SomeDelegate a = Inc;
Func<int, int> b = Inc;

is short for

SomeDelegate a = new SomeDelegate(Inc); // no cast here
Func<int, int> b = new Func<int, int>(Inc);

You can't cast an instance of SomeDelegate to a Func<int, int> for the same reason you can't cast a string to a Dictionary<int, int> -- they're different types.

This works:

Func<int, int> c = x => a(x);

which is syntactic sugar for

class MyLambda
{
   SomeDelegate a;
   public MyLambda(SomeDelegate a) { this.a = a; }
   public int Invoke(int x) { return this.a(x); }
}

Func<int, int> c = new Func<int, int>(new MyLambda(a).Invoke);
Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
dtb
  • 213,145
  • 36
  • 401
  • 431
31

Try this:

Func<int, int> c = (Func<int, int>)Delegate.CreateDelegate(typeof(Func<int, int>), 
                                                           b.Target,
                                                           b.Method);
Diego Mijelshon
  • 52,548
  • 16
  • 116
  • 154
  • Have a look at my answer for an easier way to achieve this. – Winston Smith Dec 15 '09 at 12:44
  • 1
    Ugly but useful if you can't afford the indirection of saying `Func c = b.Invoke;`. But there's one thing to remember: In .NET, all delegate types are "multicast" delegates, and the above code will only take the **last** method in the invocation list. If you can't guarantee the the invocation list has just one member, you will have to iterate through it I guess. – Jeppe Stig Nielsen Nov 05 '12 at 22:38
  • @JeppeStigNielsen I don't think a multicast `Func` makes much sense. What return value would you use? – Diego Mijelshon Nov 06 '12 at 16:32
  • 2
    You're right, it's used mostly with `void` return type, like `EventHandler` delegates with many subscribers. But try this: `Func a = () => 10; Func b = () => 20; Func c = () => 30; var multi = a + b + c; int answer = multi();` It's legal in the language and framework, so you have to make sure no-one has used it. – Jeppe Stig Nielsen Nov 06 '12 at 16:42
  • @JeppeStigNielsen it's valid but meaningless (answer will be 30, which is just as valid as 10 or 20). You can write a validation if you want. But that was not the idea of this code. – Diego Mijelshon Nov 06 '12 at 20:07
  • 1
    But the C# Language Specification says: _If the delegate invocation includes output parameters or a return value, their final value will come from the invocation of the last delegate in the list._ So all of the methods in the invocation list are called, and each might have "side effects", but the return value of the entire delegate invocation is only the return value of the **last** method in the invocation list. – Jeppe Stig Nielsen Nov 06 '12 at 20:58
  • @JeppeStigNielsen the spec says that to avoid ambiguity (i.e. so all implementations do the same). That doesn't mean it's useful. – Diego Mijelshon Nov 07 '12 at 00:24
  • 1
    @JeppeStigNielsen: More importantly, the technique above would be just as usable for void delegates as for functions, but multicast void delegates are very common. If a multicast delegate of type e.g. `Action` had its target be an `Action[]` containing constituent delegates, and had its `Method` identify a static method which takes as parameters an `Action` and a `T`, then that `CreateDelegate` usage would work just as well with muticast delegates as unicast, but alas that's not how multicast delegates were implemented. – supercat Jun 20 '14 at 19:32
  • 2
    I see [no performance gain](https://gist.github.com/RobertBouillon/ec73769e338b8ccbef8f4865c855cb15/edit) using this method in .NET Core 2.1 and Framework 4.7.2. The virtual call was slower in both by a **very** small margin (as some predicted it may be). If you need this level of optimization, the effort is probably better invested in a lower-level language, like C++. Even if you manage to optimize .NET this way, there's no guarantee it will work on all platforms or in future versions. – Robear Sep 15 '18 at 11:52
9

The problem is that:

SomeDelegate a = Inc;

Isn't actually a cast. It's the short-form of:

SomeDelegate a = new SomeDelegate(Inc);

Therefore there's no cast. A simple solution to your problem can be this (in C# 3.0)

Func<int,int> f = i=>a(i);
Gamlor
  • 12,978
  • 7
  • 43
  • 70
  • 1
    The problem with that is that you're actually wrapping the delegate with a new one that uses an anonymous method, which has a cost. – Diego Mijelshon Dec 15 '09 at 12:00
  • 2
    Yeah, you're right. Elegant Code vs. Performance. Depends on you're need what you pick. – Gamlor Dec 15 '09 at 12:13
8

This works (in C# 4.0 at least - not tried in earlier versions):

SomeDelegate a = Inc;
Func<int, int> c = new Func<int, int>(a);

If you look at the IL, this compiles into exactly the same code as Winston's answer. Here's the IL for the second line of what I just wrote:

ldloc.0
ldftn      instance int32 ConsoleApplication1.Program/SomeDelegate::Invoke(int32)
newobj     instance void class [mscorlib]System.Func`2<int32,int32>::.ctor(object, native int)

And that's also precisely what you see if you assign a.Invoke into c.

Incidentally, although Diego's solution is more efficient, in that the resulting delegate refers directly to the underlying method rather than going through the other delegate, it doesn't handle multicast delegates correctly. Winston's solution does, because it just defers completely to the other delegate. If you want a direct solution that also handles delegates with multiple targets, you need something a little more complex:

public static TResult DuplicateDelegateAs<TResult>(MulticastDelegate source)
{
    Delegate result = null;
    foreach (Delegate sourceItem in source.GetInvocationList())
    {
        var copy = Delegate.CreateDelegate(
            typeof(TResult), sourceItem.Target, sourceItem.Method);
        result = Delegate.Combine(result, copy);
    }

    return (TResult) (object) result;
}

This does the right thing for delegates with a single target by the way—it will end up producing just a single delegate of the target type that refers directly to whatever method (and where applicable, object) the input delegate referred to.

Ian Griffiths
  • 14,302
  • 2
  • 64
  • 88
  • I wonder why the `Method` and `Target` of a multi-call delegate point to one of its targets. I would think it would have made more sense to have the delegate's `Target` point to itself, and have `Method` point to either its `Invoke` method [which would check whether it was invoking itself and, if so, use the multicast list] or an "invoke multicast" method. That would have avoided the risk of accidentally turning a multi-call delegate into a single-call one. – supercat Jan 22 '13 at 21:31
  • 1
    The whole situation around `MulticastDelegate` is a mess, because Microsoft changed their minds about how to handle this fairly late in the day. In the first public preview of .NET, some delegates were multicast and some were not. They eventually decided to abandon this distinction but didn't really have time to clean things up, which left a few anomalies in the delegate type hierarchy. – Ian Griffiths Feb 20 '13 at 09:03
6

You can hack a cast by using a trick where you use the c# equivalent of a c++ union. The tricky part is the struct with two members that have a [FieldOffset(0)]:

[TestFixture]
public class Demo
{
    public void print(int i)
    {
        Console.WriteLine("Int: "+i);
    }

    private delegate void mydelegate(int i);

    [StructLayout(LayoutKind.Explicit)]
    struct funky
    {
        [FieldOffset(0)]
        public mydelegate a;
        [FieldOffset(0)]
        public System.Action<int> b;
    }

    [Test]
    public void delegatetest()
    {
        System.Action<int> f = print;
        funky myfunky;
        myfunky.a = null;
        myfunky.b = f;

        mydelegate a = myfunky.a;

        a(5);
    }
}
Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
Lucas Meijer
  • 4,424
  • 6
  • 36
  • 53
  • 3
    First time I've heard about this wonderfully dangerous hack. Thanks! – Asherah Jul 21 '14 at 06:36
  • Some of the other solutions are probably safer when the delegate types are identical, but I was trying to make Interlocked.Increment(ref int) into a "delegate int IncrementDelegate(IntPtr value)" so I could pass it a pointer to unmanaged memory, and this seems to be the safest way to do it. Certainly safer than using reflection to copy _methodPtr and _methodPtrAux fields that I was doing. With overlaying fields like this, it doesn't matter what the delegate internals are, or if they get renamed in the future. – Bryce Wagner Aug 10 '20 at 21:30
4

It is the same kind of problem as this:

public delegate int SomeDelegate1(int p);
public delegate int SomeDelegate2(int p);
...
  SomeDelegate1 a = new SomeDelegate1(Inc);
  SomeDelegate2 b = (SomeDelegate2)a;  // CS0030

which is the same kind of problem as:

public class A { int prop { get; set; } }
public class B { int prop { get; set; } }
...
  A obja = new A();
  B objb = (B)obja;  // CS0029

Objects cannot be casted from one type to an unrelated other type, even though the types are otherwise completely compatible. For lack of a better term: an object has type identity that it carries along at runtime. That identity cannot be changed after the object is created. The visible manifestation of this identity is Object.GetType().

Rob
  • 45,296
  • 24
  • 122
  • 150
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

I like examples. Here is my example code:

class Program
{
    class A
    {
        public A(D d) { d.Invoke("I'm A!"); }
        public delegate string D(string s);
    }

    class B
    {
        public delegate string D(string s);
    }
    static void Main(string[] args)
    {
        //1. Func to delegates 

        string F(dynamic s) { Console.WriteLine(s); return s; }
        Func<string, string> f = F;
        //new A(f);//Error CS1503  Argument 1: cannot convert from 'System.Func<string, string>' to 'ConsoleApp3.Program.A.D'  
        new A(new A.D(f));//I'm A!
        new A(x=>f(x));//I'm A!

        Func<string, string> f2 = s => { Console.WriteLine(s); return s; };
        //new A(f2);//Same as A(f)
        new A(new A.D(f2));//I'm A!
        new A(x => f2(x));//I'm A!

        //You can even convert between delegate types
        new A(new A.D(new B.D(f)));//I'm A!



        //2. delegate to F

        A.D d = s => { Console.WriteLine(s); return s; };

        Func<string, string> f3 = d.Invoke;
        f3("I'm f3!");//I'm f3!
        Func<string, string> f4 = new Func<string, string>(d);
        f4("I'm f4!");//I'm f4!


        Console.ReadLine();
    }
}

The output is:

enter image description here

tymtam
  • 31,798
  • 8
  • 86
  • 126