-1

As a newbie I've read about the dangers of passing parameters with the ref keyword. I guess there's great potential to mess up code when a ref variable is modified in part of a program which then changes what happens elsewhere. Objects end up being very tightly coupled. (I recognize there may be places where ref is worthwhile.) What I don't yet know, and am asking about, are the alternatives.

In one program, for example, I create a generic list at startup, which I manipulate in the program's methods. In one method:

//a user is asked a question  
//if the response is yes, the list is modified one way and the method returns true  
//if the response is no, the list is modified a different way and the method returns false. 

So the method returns a Boolean and I pass the list in as a ref. I have several similar methods, each asking users unique questions and then modifying the list in some way.

It seems like a typical alternative might be to bundle the list and a Boolean field into its own class. Somehow this seems like creating an object for nothing more than convenience, just to hold two pieces of data, with no connection to any real world entity.

So, how would you (pseudo)code a method that returns both a generic list and a Boolean value?


EDIT: Here's some actual code

 private static bool AskExptQuestion(ref List<StatTest> testList)
        {
            Console.Write(Constants.ExptQText); //experimental groups?
            string response = Console.ReadLine();

            //if response==y, it's experimental 
            if (response == "y")
            {
                //so select all experimental
                var q1List =
                from test in testList
                where test.isExperimental == true
                select test;

                //to copy resulting IEnumerable<List> (q1list) to generic List, must copy/cast IEnumerable to a List<t>
                testList = q1List.ToList();
                return true;
            }
            //and if response==n, it's not experimental 
            else
            {
                //so select all non-experimental
                var q1List =
                from test in testList
                where test.isExperimental == false
                select test;

                testList = q1List.ToList();
                return false;
            }

        }
Community
  • 1
  • 1
Al C
  • 5,175
  • 6
  • 44
  • 74
  • 2
    Why do you pass the list in as a `ref`? Just modify it in place, e.g. with… `AddRange`? – Ry- Sep 11 '16 at 20:44
  • 4
    Just get some code working first and worry about "dangers" later. You're off the track here. – H H Sep 11 '16 at 20:45
  • Objects are already pass by value, so you don't need to use the ref keyword to pass a collection into a method for modification. –  Sep 11 '16 at 20:47
  • You don't need to past a list as `ref` to be able to modify it. You can do that just fine without `ref`. – JLRishe Sep 11 '16 at 20:47
  • @Ryan Not sure I understand ... The list is essentially like a global variable. If I don't pass it as a ref it won't be modified in a way visible to the rest of the program. – Al C Sep 11 '16 at 20:50
  • 2
    @AlC: You can modify the *object* that the variable refers to without changing the value of the variable. It sounds like you need to read http://pobox.com/~skeet/csharp/parameters.html – Jon Skeet Sep 11 '16 at 20:52
  • @AlC: Could you show some code that will help us understand what you’re doing with the list, please? This is a bit too vague to answer right now. You might want to keep `ref`, you might want to remove it, you might want to create a class that properly represents whatever the list does. Also, `ref`/`out` are pretty much extra return values and I don’t see how they could influence coupling any more than returning multiple values as a tuple. – Ry- Sep 11 '16 at 20:52
  • @HenkHolterman I appreciate your response. And the code's working OK now. I just prefer the idea of using best practices from the get go, and understanding why they are "best." – Al C Sep 11 '16 at 20:52
  • 1
    Also, note that the quoted "AVOID using `out` or `ref` parameters." section in the answer to the question you linked is silly. Ignore it. – Ry- Sep 11 '16 at 21:05
  • @Ryan ... I figured it out. I'm replacing the object in my code right now. That's why it won't "work" as hoped if I don't use the `ref` keyword. I didn't understand that part before posting! – Al C Sep 11 '16 at 21:25
  • Since you began by asking for alternatives, one would be: make the method an instance method and set properties instead. That might lead to other refactorings, too. The method right now is just a chunk of code doing many different things: user interaction, business logic, data querying, …. – Tom Blodget Sep 11 '16 at 21:32

3 Answers3

1

Returning a list (or almost anything else, for that matter) along with its characteristic, such as a Boolean value, is a "poster child" of the ref/out feature. This pattern is used in several places in the standard .NET library:

  • Dictionary.TryGetValue uses this pattern, returning true or false depending on presence of the key in the dictionary, and setting a out to the return object
  • Integer.TryParse (and other numeric types) use this pattern, returning true when the parse is successful, and setting the value in a out parameter

The difference between ref and out is that ref gives your method an option to keep the old object/value or to supply a new one, while the out forces you to supply a new one before the method returns.

There is no point to create a new class simply to bundle two unrelated types together.

In addition, it is important to understand that modifications to method parameters can happen even in situations when a parameter is not passed by ref or out. When you pass an object of reference (i.e. class) type that is mutable, any modifications to the object done inside the method are visible in the caller.

The only difference when passing by ref or out is that you are allowed to replace the object itself with a new one, while passing a reference type without ref is restricted to mutating the incoming object itself.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • To be pedantic, `TryGetValue` and `TryParse` use `out`, not `ref`. – Kenneth K. Sep 11 '16 at 20:55
  • @KennethK. Fixed. The two are very closely related. – Sergey Kalinichenko Sep 11 '16 at 20:57
  • 1
    Minor point again, `ref` implies that the object has already been instantiated outside of the method; `out` implies that the method itself will do the instantiating. You're not really replacing anything with `out` because you can't guarantee that the caller instantiated the variable to begin with. – Kenneth K. Sep 11 '16 at 21:11
  • 1
    @KennethK. for `out` initialization inside the method is not only implied, it is required. [CS0269 Use of unassigned out parameter, CS0177 The out parameter must be assigned to before control leaves the current method] (Whatever value it might have had before the call can't be accessed via the parameter inside the method.) – Tom Blodget Sep 11 '16 at 21:25
1

From reading your example and comments, it sounds like you might just want the ability to apply some filters to a collection. Why not have your functions return the filters?

private static Predicate<StatTest> AskExptQuestion()
{
    Console.Write(Constants.ExptQText);  // experimental groups?
    bool response = Console.ReadLine() == "y";  // maybe wrap this up in a function to read a yes/no answer
    return t => t.isExperimental == response;
}

You might still need that bool return value, though, which can become an out parameter. It’s not clear what that’s for.

Ry-
  • 218,210
  • 55
  • 464
  • 476
-3

in c# paramaters are passed by value. object types (classes) are implimented by a pointer to an instance in memory. where passing an object to a method, the pointer is copied to the argument and he then points to the same instance.

if i have:

void foo(MyClass param)
{
    param.x = 7;
}

and somewhere else i do:

MyClass obj = new MyClass();
obj.x = 5;
foo(obj);

then after the call to foo() the x property of obj is 7.

if foo is:

void foo(MyClass param)
{
    param = new MyClass();
    param = 7;
}

then my original obj will still have x equals to 5. this is because i ran over the instance that param was looking at.

now the "ref" keyword:

void foo(ref MyClass param)
{
    param = new MyClass();
    param = 7;
}

if i call like this:

MyClass obj = new MyClass();
obj.x = 5;
foo(ref obj);

in this combination my obj will be set to the new instance of MyClass and will have x equals 7. the ref means that the variable in the method is the same as the variable passed, and not just pointing to the same instance to start with.

Imbar M.
  • 1,074
  • 1
  • 10
  • 19
  • 3
    "in c# objects are passed by reference" - no, references are passed by value. That's not the same thing, and it's worth being precise about this. – Jon Skeet Sep 11 '16 at 20:58