Cast delegate to Func in C#

C#.NetCastingDelegates

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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.

Solution 2 - C#

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);

Solution 3 - C#

Try this:

Func<int, int> c = (Func<int, int>)Delegate.CreateDelegate(typeof(Func<int, int>), 
                                                           b.Target,
                                                           b.Method);

Solution 4 - C#

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);

Solution 5 - C#

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.

Solution 6 - C#

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);
	}
}

Solution 7 - C#

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().

Solution 8 - C#

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndreyAkinshinView Question on Stackoverflow
Solution 1 - C#Winston SmithView Answer on Stackoverflow
Solution 2 - C#dtbView Answer on Stackoverflow
Solution 3 - C#Diego MijelshonView Answer on Stackoverflow
Solution 4 - C#GamlorView Answer on Stackoverflow
Solution 5 - C#Ian GriffithsView Answer on Stackoverflow
Solution 6 - C#Lucas MeijerView Answer on Stackoverflow
Solution 7 - C#Hans PassantView Answer on Stackoverflow
Solution 8 - C#tymtamView Answer on Stackoverflow