Search code examples
c#out

Reference type function argument does not retain changes after function try catch block


Code below.

I completely understand reference vs value types. [correction, I do not completely understand reference vs value types.]

Today I ran into something odd.
I declare a datatable class var and initialize it as null.
I create function: GetAutoAllocationResult(in int, datatable, out string)
I pass pass the datatable class var to the function.
I make some conditional changes inside a TRY/CATCH block.
I return from the function.
The datatable does not retain changes from within the function.
If I use the OUT keyword in the function signature for the datatable, it retains changes.
What?

DataTable auto_allocation_result = null;

// ...


// get the data from the stored procedure return
string auto_allocation_query_result_message = string.Empty;
bool is_auto_allocation_result_query_successful = false;
is_auto_allocation_result_query_successful = GetAutoAllocationResult(auto_allocation_record_uid, 
                        auto_allocation_result, out auto_allocation_query_result_message);

//...


private bool GetAutoAllocationResult(in int p_auto_allocate_record_uid, DataTable p_return_data, string p_message)
{
    bool is_successful = false;
    p_return_data = new DataTable();
    p_message = string.Empty;

    string sql = some select statement

    SqlCommand sc = new SqlCommand();
    sc.Connection = some sql connection
    sc.CommandText = sql;


    // add parameters
    SqlParameter sqlparam_order_ship_loc_uid = sc.Parameters.Add("@order_ship_loc_uid", SqlDbType.Int);
    sqlparam_order_ship_loc_uid.Value = p_auto_allocate_record_uid;
    


    // run query
    try
    {
        SqlDataAdapter sda = new SqlDataAdapter(sc);
        sda.Fill(p_return_data);   // p_return_data DOES NOT RETAIN DATA AFTER FUNCTION CALL IF OUT KEYWORD IS NOT USED

        if (p_return_data != null)
        {
            if (p_return_data.Rows.Count <= 0)
            {
                p_return_data = null;

                p_message =
                    $"Could not perform auto-allocation.  Please provide IT with the information below.{Environment.NewLine}" +
                    $"{Environment.NewLine}" +
                    $"No auto allocation rows returned for UID ({p_auto_allocate_record_uid}).";

                is_successful = false;
            }
            else
            {
                is_successful = true;
            }
        }
        else
        {
            p_message =
                $"Could not perform auto-allocation.  Please provide IT with the information below.{Environment.NewLine}" +
                $"{Environment.NewLine}" +
                $"Auto allocation query did not instantiate a datatable ({p_auto_allocate_record_uid}).";

            is_successful = false;
        }
    }
    catch (Exception ex)
    {
        p_message =
            $"Failed to perform auto-allocation.  Please provide IT with the information below.{Environment.NewLine}" +
            $"{Environment.NewLine}" +
            $"There was an error querying auto allocation result data.{Environment.NewLine}" +
            $"Exception Message: ({ex.Message}){Environment.NewLine}" +
            $"Inner Exception: ({ex.InnerException}){Environment.NewLine}" +
            $"Stack Trace: ({ex.StackTrace})";

        is_successful = false;
    }


    return is_successful;            
}


Solution

  • You need to set it to the new DataTable rather than null outside the function. Remember a reference is a pointer to a memory object. But the reference itself is passed by value (on the stack/copied). So you've passed in a reference that doesn't point to a memory object (null) and the copy has been assigned to the DataTable inside the function. When the function finishes the copy is removed from the stack/disgarded and the DataTable goes to the garbage collector.

    It's an easy mistake to make if you're not experienced. But now you should have a better understanding for your future coding fun.