7

In a C# program I've made a method that deletes an object from a list. The user enters the index of the item to be deleted, the user is then asked to confirm the deletion, and the item is removed from the list if the user confirms, otherwise the list remains the same.
I'm not sure about the best way to pass arguments to the method. I tried passing the list by reference (as an out parameter):

static void DeleteCustomer(out List<Customer> customers)
{
    // ...display list of objects for user to choose from...
    int deleteId = ReadInt("Enter ID of customer to delete: ");
    Console.Write("Are you sure you want to delete this customer?");
    if (Console.ReadLine().ToLower() == "y")
    {
        customers.RemoveAt(deleteId);
    }
}

The above code doesn't work as I get the errors Use of unassigned local variable 'customers' and The out parameter 'customers' must be assigned to before control leaves the current method. I was thinking I could pass the list by value and return the same list, like this:

static List<Customer> DeleteCustomer(List<Customer> customers)
{
    int deleteId = ReadInt("Enter ID of customer to delete: ");
    Console.Write("Are you sure you want to delete this customer?");
    if (Console.ReadLine().ToLower() == "y")
    {
        customers.RemoveAt(deleteId);
    }
    return customers;
}

// ...which would be called from another method with:
List<Customer> customers = DeleteCustomer(customers);

but this doesn't seem efficient as the same variable is passed by value and then returned.

What is the most efficient way to pass arguments in this case?

Ruben9922
  • 766
  • 1
  • 11
  • 16
  • 1
    List is passed by reference automatically so no need to return it. The calling method will see the change reflected in its reference to the passed List. – user469104 Dec 02 '14 at 18:27
  • 2
    See: [Parameter passing in C# - By Jon Skeet](http://www.yoda.arachsys.com/csharp/parameters.html). Also try the second method, without returning the list, you will see that it has been changed. That is because the address of reference type is passed by value. **But importantly** , you shouldn't worry about efficiency *now*, consider writing code which conveys the intent more clearly and only later look for improving performance. – Habib Dec 02 '14 at 18:29
  • Second method could have a return type of `void`. – Drew Kennedy Dec 02 '14 at 18:31
  • 1
    @user469104 Its passing a reference, which isn't quite saying the same thing as the reference being passed by reference. I get what you are saying though. – BradleyDotNET Dec 02 '14 at 18:32
  • @BradleyDotNET - you are correct, should have said 'passed as a reference to the list' rather than 'by reference' – user469104 Dec 02 '14 at 18:40

2 Answers2

5

List like all reference types, is passed as a reference to the object, and not a copy of it.

Note that this is very different from saying it is passed by reference, as that would imply assignment of the parameter propagates to the caller, which it does not

It does means that modifications to the object (such as those performed by RemoveAt) will automatically propagate to the caller.

Thus, just pass it; no need for a return value or out/ref parameters.

You will very rarely use out/ref for reference types, and when used for value types, the performance difference will be so small versus returning that you shouldn't worry about it unless you have profiled and made sure that the problem occurs there. Use what makes the most idiomatic sense.

BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • 2
    `List like all reference types, is passed as a reference to the object, and not a copy of it. This means that modifications will automatically propagate to the caller`. This is not correct. You can't assign `null` or a new instance to your passed object. Reference type's address is passed by value. – Habib Dec 02 '14 at 18:33
  • 1
    @Habib Totally agreed. It is passed *as* a reference; not *by* reference. Passing a reference by reference is different. Do you have a wording that makes more sense to you? Sometimes I really wish we had pointers, it is so much easier to explain... I also edited (while you were typing?) to clarify the propagation statement. – BradleyDotNET Dec 02 '14 at 18:34
  • 3
    Its a tricky statement, I am more inclined to say that nothing in C# gets passed by reference unless ref/out keyword is used. For Reference types, reference/address is passed as value. – Habib Dec 02 '14 at 18:36
  • @Habib Thanks for the feedback, I added a clarification, and am thinking of a way to include your wording as well. – BradleyDotNET Dec 02 '14 at 18:39
  • @Habib so if I byRef a `List` and set it to a `new List()`, will the calling function now have the new list? (I know this isn't a smart way to do things. Just want ot make sure I understand) – hometoast Dec 02 '14 at 19:11
  • 1
    @hometoast, yes. You will see the change. The calling function will have a new list. – Habib Dec 02 '14 at 19:19
2

In C# the parameter are passed by value. This mean that when you pass a parameter to a method, a copy of the parameter is passed. C# have types by value (like int) and by reference (like any class). C# contains an stack (when push all varaibles) and a Heap. The value of the value types are pushing directly in this stack, while the reference of the reference type are push in the stack, and the referenced value are pushed in the Heap.
When you pass a reference type (like a List) it make a copy of the reference, but this copy point to the same object into the list. Therefore any change affect directly to the object, unless you change the reference (with an assigmet), but this is not your case.

this could by your code:

    static void DeleteCustomer<T>(List<T> customers)
    {
        Console.WriteLine("Enter ID of customer to delete: ");
        int deleteId;
        if (int.TryParse(Console.ReadLine(), out deleteId)) // if the input is an int
        {
            Console.Write("Are you sure you want to delete this customer?");
            if (Console.ReadLine().ToLower() == "y")
            {
                customers.RemoveAt(deleteId);
            }
        }
        else
        {
            Console.WriteLine("This is not valid Id");
        }
    }

If you want to know about ref an out keyword i can help you too, but for this example is not neccesary.

Alexander Leyva Caro
  • 1,183
  • 1
  • 12
  • 21