Martin
Martin

Reputation: 161

Lambda in C# and ref/out

I have a few but similar set/get functions imported from a DLL that interface with a communication bus. These all should ideally share an identical wrapper doing some error checks, mutex handling etc. The code snippet below is a basic representation of it. It compiles and all functions seem to work except that WrappedGet() does not modify the referenced variable data. Ideas?

        static UInt32 common_var = 0;

        // Reduce boilerplate by wrapping all calls to the DLL with this
        static private bool CommonWrapper<T1>(Action<T1> dll_function_handle, ref T1 data)
        {
            // Commmon code for mutex handling etc
            /// .... 
            
            // Call to function handle, sometimes data is in-parameter, other times out-param
            dll_function_handle(data);
            
            return true;
        }

        static private void DllSet(UInt32 data) { common_var = data; }
        static public void WrappedSet(UInt32 data) { CommonWrapper<UInt32>((UInt32 a) => DllSet(a), ref data); }

        static private void DllGet(ref UInt16 data){ data = 1234; }
        // WrappedGet does not modify argument "data" because of the lambda? What to do?
        static public void WrappedGet(ref UInt16 data) { CommonWrapper<UInt16>((UInt16 a) => DllGet(ref a), ref data); }

EDIT:

This works:

  static public void WrappedGet(ref UInt16 data) 
  {
       UInt16 local = 0;
       CommonWrapper<UInt16>((UInt16) => DllGet(ref local), ref data);
       data = local;
  }

It is a bit awkward though, is it the right way to do this?

Upvotes: 0

Views: 159

Answers (2)

Rubidium 37
Rubidium 37

Reputation: 711

Following my comment, this is the solution most similar to your original code:

class TEST
{
    static UInt32 common_var = 0;

    // Reduce boilerplate by wrapping all calls to the DLL with this
    static private bool CommonWrapper<T1>(Action<T1> dll_function_handle, ref T1 data)
    {
        dll_function_handle(data);

        return true;
    }
    static private bool CommonWrapper<T1>(Func<T1, T1> dll_function_handle, ref T1 data)
    {
        data = dll_function_handle(data);

        return true;
    }

    static private void DllSet(UInt32 data) { common_var = data; }
    static public void WrappedSet(UInt32 data) { CommonWrapper<UInt32>((UInt32 a) => DllSet(a), ref data); }

    static private void DllGet(ref UInt16 data) { data = 1234; }
    static public void WrappedGet(ref UInt16 data) { CommonWrapper<UInt16>((UInt16 a) => { DllGet(ref a); return a; }, ref data); }
}

Personally, I would avoid using a ref parameter if any input value is discarded. I would instead declare the parameter as out or declare a return type for the method; this way, the Func<T1,T1> should be replacable with the simpler Func<T1>.

Upvotes: 1

MarioCake
MarioCake

Reputation: 61

The problem in you code is that the Action does not take the parameter as a ref so a copy of data will be created before it is given to the DllGet method.

//                    vvvvvvvvvvvvvvvvvvvvvvvvvvv 
CommonWrapper<UInt16>((UInt16 a) => DllGet(ref a), ref data);

Instead of an Action you could define a delegate and use a ref in the lambda.

So you need to add an delegate

private delegate void WrappedFunction<T>(ref T data);

And need to use it in the CommonWrapper

//                                    vvvvvvvvvvvvvvvvvvv
static private bool CommonWrapper<T1>(WrappedFunction<T1> dll_function_handle, ref T1 data)
{
    // Your code here
}

And you need to add the ref to the lambda

//                                                                      vvv
static public void WrappedGet(ref UInt16 data) { CommonWrapper<UInt16>((ref UInt16 a) => DllGet(ref a), ref data); }

So the whole code looks like this:

private delegate void WrappedFunction<T>(ref T data);

// Reduce boilerplate by wrapping all calls to the DLL with this
static private bool CommonWrapper<T1>(WrappedFunction<T1> dll_function_handle, ref T1 data)
{
    // Commmon code for mutex handling etc
    // .... 
    
    // Call to function handle, sometimes data is in-parameter, other times out-param
    dll_function_handle(ref data);
    
    return true;
}

static private void DllSet(UInt32 data) { common_var = data; }
static public void WrappedSet(UInt32 data) { CommonWrapper<UInt32>((ref UInt32 a) => DllSet(a), ref data); }

static private void DllGet(ref UInt16 data){ data = 1234; }
static public void WrappedGet(ref UInt16 data) { CommonWrapper<UInt16>((ref UInt16 a) => DllGet(ref a), ref data); }

Upvotes: 2

Related Questions