Well-performing Action cast

       public interface ifX {}
       public struct X : ifX { public string myName; }
       public struct Y : ifX { public string myName; }
       public struct Z : ifX { public string myName; }

     
       // "Two" throws:
       public static Action<ifX>[] cc = {
           One,
           Two,       // Compiler error: Expected a method with 'void Two(ifX)' signature
           _x => Three((Z)_x)       // Its a solution. But is it performant
       };

       // Currently:
       static void One(ifX item1)
       {
           var myItem = (X)item1;
           Debug.Log(myItem.myName);
       }

       // Preferably:
       static void Two(Y item2)
       {
           Debug.Log(item2.myName);
       }

       // Dirty walkaround (see in declaration)
       static void Three(Z item2)
       {
           Debug.Log(item2.myName);
       }

       static void DoStuff()
       {
           var xItem = new X();
           cc[0].Invoke(xItem);

           var yItem = new Y();
           cc[1].Invoke(yItem);

           var zItem = new Z();
           cc[2].Invoke(zItem);
       }

Above is an oversimplified demo of what I’m trying to accomplish. Currently the standard way would be invocation of One(ifX), preferably I would invoke it as Two(Y), there is a workaround at _x => Three((Z)_x) but I think performance will be terrible is there a better way, and if not, how performant is that particular solution?

The safety guarantees of .Net require you to have a cast, since it cannot ascertain that the Two Action delegate will always be called with a Y object (it could be called with any ifX implementation).

There’s basically three points where you can do the cast:

  • Only keep object references of the delegates and then cast them to the right Action type before using them
  • Wrap the delegate and do the cast there (as you suggested as workaround)
  • Change Two to take an ifX argument and do the cast inside the method

It really depends on your use case if the wrapping would ever become a performance problem. Usually it’s only code that gets called many times per frame that becomes a problem and your optimization should be guided by profiling instead of assumptions.