33

I'm trying to create a delegate of a static method which takes a ref argument. Please don't ask why I'm doing such a cockamamie thing. It's all part of learning how .Net, C#, and reflection work and how to optimize it.

My code is:

    public struct DataRow
    {

        private double t;
        static public void Cram_T(ref DataRow dr, double a_t)
        {
            dr.t = a_t;
        }
    }
 ''''
  Type myType = typeof(DataRow);
  MethodInfo my_Cram_T_Method = myType.GetMethod("Cram_T");
  var myCram_T_Delegate = 
         Delegate.CreateDelegate(typeof(Action<DataRow, Double>),      
                                 my_Cram_T_Method) 
                                 as Action<DataRow, Double>;

This gives me a binding error because (I think) the generic action doesn't match the method.

Inspecting the value of Cram_T_Method in the watch window gives

{Void Cram_T(DataRow ByRef, Double)}

I then tried using the ref keyword in the Action:

  var myCram_T_Delegate = 
         Delegate.CreateDelegate(typeof(Action<ref DataRow, Double>),         
                                 my_Cram_T_Method) 
                                 as Action<ref DataRow, Double>;

But this won't compile. The C# compiler chokes at the token "ref".

What is the right way to create this delegate?

Max Yaffe
  • 1,317
  • 1
  • 14
  • 26

2 Answers2

46

Create your own delegate type:

delegate void MyAction(ref DataRow dataRow, double doubleValue);

And use MyAction in place of Action<ref DataRow, Double> -- which, as you've noted, doesn't compile.

Ben M
  • 22,262
  • 3
  • 67
  • 71
28

@Ben M has the right idea, although you could make it more generic:

public delegate void RefAction<T1, T2>(ref T1 arg1, T2 arg2)

The problem has nothing to do with delegates as such - it's just that you can't use ref when you specify a type argument.

In theory the "it's by ref or not" is part of the type information (hence Type.IsByRef) but you can't specify it like that.

Frankly I'm not at all sure what would happen if you tried to create a List<ref int> via reflection, for example - I would hope that an exception would be thrown... it's not a very sensible concept :)

EDIT: I've just tried it:

Type refInt = typeof(int).MakeByRefType();
Type refIntList = typeof(List<>).MakeGenericType(refInt);

Throws an error:

Unhandled Exception: System.ArgumentException: The type 'System.Int32&' may
not be used as a type argument.
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 5
    Type arguments must be types whose values are convertible to object. Managed and unmanaged pointer types don't meet that requirement. – Eric Lippert Jan 08 '10 at 20:31
  • 2
    Well I never. Fascinating. For once, I *don't* think it'll be worth including that in C# in Depth. Maybe as a note... – Jon Skeet Jan 08 '10 at 21:04
  • This works but the list commentary is irrelevant. public delegate void RefGenAction(ref T1 arg1, T2 arg2); var myCram_TDelegate = Delegate.CreateDelegate(typeof(RefGenAction), myType.GetMethod("Cram_T")) as RefGenAction; – Max Yaffe Jan 08 '10 at 22:00
  • 3
    @Max Yaffe: In what way is the list commentary irrelevant? It's making the point that you can never use "ref Foo" as a generic type argument, whether it's for a delegate or not. – Jon Skeet Jan 08 '10 at 22:46
  • The VB.NET equivalent, adding a type constraint to type T1: `Public Delegate Sub RefAction(Of T1 As Class, T2)(ByRef arg1 As T1, ByVal arg2 As T2)` – MCattle Apr 30 '13 at 17:00