views:

202

answers:

2

So if I am iterating using a foreach loop and I have a function inside that takes an argument of the object iterated from list, and lets say i set its value to be different. Why don't I have to use out or ref ? I thought it was only passed by value if it you didn't use out or ref.... I know that a ref you must have initialized the variable before and out you just have to have set its value before return from the method.

It seems like if you a iterating thru a list and pass an object in its actually passed by reference. Consider the following example.

Example

class Program
    {
        static void Main(string[] args)
        {

            List<Foo> list = new List<Foo>();
            list.Add(new Foo() { Bar = "1" });
            list.Add(new Foo() { Bar = "2" });



            foreach (var f in list)
            {
                Foo f2 = f; 
                Console.WriteLine("SetFoo Pre: " + f2.Bar);
                SetFoo(f2);
                Console.WriteLine("SetFoo Post: " + f2.Bar);

                Console.WriteLine("SetFooRef Pre: " + f2.Bar);
                SetFooRef(ref f2);
                Console.WriteLine("SetFooRef Post: " + f2.Bar);
                Console.WriteLine("");
            }




            Console.WriteLine("");

            int i = 0;
            // Not using ref keyword
            Console.WriteLine("SetI Pre: " + i);
            SetI(i);
            Console.WriteLine("SetI Post: " + i);

            // Using ref keyword
            Console.WriteLine("SetRefI Pre: " + i);
            SetRefI(ref i);
            Console.WriteLine("SetRefI Post: " + i);
        }


        private static void SetRefI(ref int i)
        {
            i = 3;
            Console.WriteLine("SetRefI Inside: " + i);
        }

        private static void SetI(int i)
        {
            i = 2;
            Console.WriteLine("SetI Inside: " + i);
        }

        private static void SetFooRef(ref Foo f)
        {
            f.Bar = String.Format("{0} :: {1}", f.Bar, "WithRef");
            Console.WriteLine("SetFooRef Inside: " + f.Bar);
        }

        private static void SetFoo(Foo f)
        {
            f.Bar = String.Format("{0} :: {1}", f.Bar, "WithoutRef");
            Console.WriteLine("SetFoo Inside: " + f.Bar);
        }
    }


    class Foo
    {
        public string Bar { get; set; }
    }

Output:

SetFoo Pre: 1 SetFoo Inside: 1 ::
WithoutRef SetFoo Post: 1 WithoutRef
SetFoo Pre: 1 :: WithoutRef SetFoo
Inside: 1 :: WithoutRef :: WithRef
SetFoo Post: 1 WithoutRef :: WithRef

SetFoo Pre: 2 SetFoo Inside: 2 ::
WithoutRef SetFoo Post: 2 WithoutRef
SetFoo Pre: 2 :: WithoutRef SetFoo
Inside: 2 :: WithoutRef :: WithRef
SetFoo Post: 2 WithoutRef :: WithRef

SetI Pre: 0 SetI Inside: 2 SetIPost: 0

SetRefI Pre: 0 SetRefI Inside: 3
SetRefI Post: 3

I understand the ref with the integer example but not the above Foo object iteration example.

Thanks!

+8  A: 

The reference is passed by value. So the method can still change the contents of the object, it just can't change which object your variable refers to.

See my article on parameter passing for a lot more information, along with my article about reference types and value types.

Jon Skeet
So in the case above, I am pass a refence of the object Foo to my SetFoo methods. Since it shares the sames memory location, if I make a change to a value of Bar, the value of Bar will be the same of that instance of Foo in the list after exiting my function ? So there is no need for my SetFooRef method then ?
gmcalab
It is still different, you pass and object by reference value rather than object reference pointer (so to speak) if your are not using ref modifier. Your SetFooRef can be usefull if, say, you will construct a completely new Foo object inside the method - this new object will be "seen" outside the method. In contrast, without ref, you can change object properties, etc. and those changes will be "seen" outside, but if you initialize a new Foo object on a parameter variable - it will only have local scope as you will overwrite reference value.
Audrius
Ok that makes more sense. So for the integer example I am passing it by value with SetI() but set SetRefI() I am passing by reference pointer then ?
gmcalab
With SetRefI you are passing the argument by reference, yes. Don't start using the word pointer, it'll only confusing things :)
Jon Skeet
OK, well I guess i was just confused on why not using ref on int changing the value inside the function will not be reflected outside, but passing an object without ref the changes would be see post function
gmcalab
gmcalab: Have you now read the value type / reference type page I linked to? That should have cleared things up a bit.
Jon Skeet
A: 

A Reference Type object will be sent as "ref" even if you don't specify. If you create a custom class/object, for the most part, it will be a reference type.

Eclipsed4utoo
No, it won't be sent as ref. There's a big difference between (say) "void Foo(ref string x)" and "void Foo(string x)". And if you create a class, it will *always* be a reference type.
Jon Skeet
String is an exception to the rule since it's a reference type by acts as a value type. So, of course, passing a string without "ref" and with "ref" will be different. Would you say that passing a StringBuilder object without "ref" and with "ref" is a "big difference"? It's passed by reference whether "ref" is specified or not.
Eclipsed4utoo
No, string acts like a reference type - it just happens to be immutable. It's not a special case. And yes, whether you have "ref" on a StringBuilder parameter *does* make a difference. Read the article I linked to.
Jon Skeet
@Eclipsed try calling `void Foo(string x){x="Z";}` from `void Bar(){var s = "A";Foo(s);Console.WriteLine(s);}` Now add `ref` to Foo's signature and run it again. Notice the difference. It's the same with any reference type.
statenjason