• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 877
  • Last Modified:

ref keyword in foreach statement

Can ref keyword be used in foreach statement? i.e. where should I put it in the following example?

foreach (MyClass myClass in myClasses)
{
   func1( ref myClass);
}

Thanks.
0
gromul
Asked:
gromul
2 Solutions
 
gregoryyoungCommented:
Your iteration is readonly (i.e. you could change properties of the reference etc but not the data itself).

If you need to change the data itself use a standard for loop

for(int i=0;i<MyClasses.Length;i++) {
    func1(ref MyClasses[i]);
}

which is allowed.

Cheers,

Greg Young
0
 
gromulAuthor Commented:
It seems ref can't be used in foreach statement. Is there a way around this? I tried the following:

for(int i=0; i<myClasses.count; i++)
{
   func1(ref myClasses[i]);
}

but I'm getting the following error:

"A property or indexer may not be passed as an out or ref parameter"

I don't want to create new myClass objects because they are relatively big.
0
 
gromulAuthor Commented:
I guess we posted at about the same time.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
gregoryyoungCommented:
Sorry I didn't realize you had a double whammy (thought you might be using an array).

A property actually can be used but it requires you to jump through some hoops ... keep in mind that ref is setting the variable for you where as a property is a set of methods that are called (big difference). Eric Gunnerson actually discusses briefly why it is this way here http://blogs.msdn.com/ericgu/archive/2004/03/01/82336.aspx

http://blogs.msdn.com/jmstall/archive/2006/02/09/property_by_Ref.aspx includes a way you can work around it .. but I would recommend using a variable intermediately instead (you'll see why :) )
0
 
gromulAuthor Commented:
I'm not familiar with the term you used. What's a double whammy?
0
 
gromulAuthor Commented:
Another possible solution that occured to me is something like this:

for(int i=0; i<myClasses.count; i++)
{
   func1(ref myClasses, int index);
}

and in func1 operate on a single myClass by using myClasses[index].

Is this more efficient than the code below?

for(int i=0; i<myClasses.count; i++)
{
   MyClass temp = myClasses[i];
   func1(ref temp);
   // no need for "myClasses[i] = temp;" because I'm just extracting some values contained in myClass
}
0
 
gregoryyoungCommented:
double whammy .. slang ..

There were two reasons why your code wasn't working (readonly iteration and property).

After seeing this code I am curious if you have a misconception about what ref does.


for(int i=0; i<myClasses.count; i++)
{
   MyClass temp = myClasses[i];
   func1(ref temp);
   // no need for "myClasses[i] = temp;" because I'm just extracting some values contained in myClass
}

if you have no need for setting temp back .. then the following code is identical

for(int i=0; i<myClasses.count; i++)
{
   func1(temp);
}

changing the function to not be a ref.
0
 
anyoneisCommented:
I'm confused by this thread. Clearly, you can't pass an iteration variable by ref, because you don't own the reference to the iteration variable - I think everyone sees this.

However, there seems to be some implication that you are protecting the contents of an instance if you don't pass it by ref, and that is false. The contents are mutable whether passed by value or by reference.

If this is clear to all then I apologize for jumping in. Otherwise, it would be good to ensure that the output of this program is not at all surprising:

using System;
using System.Collections.Generic;
using System.Text;

namespace ForEachRef
{
    class MyClass
    {
        static int nextID = 0;
       
        public int ID;
        public string Data;

        public MyClass()
        {
            ID = nextID++;
            Data = "ID: " + ID;
            Console.WriteLine("Construct " + this);
        }
        public MyClass(MyClass other)
        {
            ID = nextID++;
            Data = other.Data;
            Console.WriteLine("Copy Construct " + this);
        }
        public override string ToString()
        {
            return string.Format("MyClass ID: {0}; Data: {1}", ID, Data);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            MyClass[] myThings = new MyClass[2];
            for (int i = 0; i < myThings.Length; i++)
                myThings[i] = new MyClass();

            foreach (MyClass mc in myThings)
            {
                Console.WriteLine("Call Func1 on " + mc);
                func1(mc);
                //func2(ref mc);
                //Error      1      Cannot pass 'mc' as a ref or out argument because it is a 'foreach iteration variable'      
            }

            for (int i = 0; i < myThings.Length; i++)
            {
                Console.WriteLine("Call Func2 on " + myThings[i]);
                func2(ref myThings[i]);
            }

            foreach (MyClass mc in myThings)
            {
                Console.WriteLine("Examine Results " + mc);
            }

            Console.ReadKey();
        }
        static void func1(MyClass myThing)
        {
            myThing.Data = "Func1";
            Console.WriteLine("Func1 with " + myThing);
        }
        static void func2(ref MyClass myThing)
        {
            myThing.Data = "Func2";
            myThing = new MyClass();
            myThing.Data = "Func2";
            Console.WriteLine("Func2 with " + myThing);
        }
    }
}

/* Output

Construct MyClass ID: 0; Data: ID: 0
Construct MyClass ID: 1; Data: ID: 1
Call Func1 on MyClass ID: 0; Data: ID: 0
Func1 with MyClass ID: 0; Data: Func1
Call Func1 on MyClass ID: 1; Data: ID: 1
Func1 with MyClass ID: 1; Data: Func1
Call Func2 on MyClass ID: 0; Data: Func1
Construct MyClass ID: 2; Data: ID: 2
Func2 with MyClass ID: 2; Data: Func2
Call Func2 on MyClass ID: 1; Data: Func1
Construct MyClass ID: 3; Data: ID: 3
Func2 with MyClass ID: 3; Data: Func2
Examine Results MyClass ID: 2; Data: Func2
Examine Results MyClass ID: 3; Data: Func2

*/

I was mildly surprised because I thought I read somewhere that copies were made becuase of the foreach. Apparently not. Only the reference is "copied" to the iteration variable.

David
0
 
dstanley9Commented:
I think anyoneis is right.  The ref keyword when used with reference types protects which instance the variable is pointing to, not the instance itself.

So this is completely valid:

ArrayList al = new ArrayList();
func1(al);  
Console.WriteLine(al[0]);  // "Hello"

public void func1(ArrayList al)  // Notice no ref
{
 al.Add("Hello");
}

// However, this is not legal
public void func1(ArrayList al)  // Need ref keyword to make this work
{
  al = new ArrayList();  // changing what the al parameter points to
}
0
 
gregoryyoungCommented:
is that not what I said as well? :) see my last post.

Cheers,

Greg
0
 
gromulAuthor Commented:
I'm not really concerned with protecting the object I want to pass as ref. I'm just interested in what's the most efficient to do it.

You're saying that this

for(int i=0; i<myClasses.count; i++)
{
   MyClass temp = myClasses[i];
   func1(temp);
}

is identical to

for(int i=0; i<myClasses.count; i++)
{
   MyClass temp = myClasses[i];
   func1(ref temp);
}

My understanding is that passing temp by ref would be more efficient because only its address is passed and the function can operate directly on the object, so another object isn't created. Am I wrong in assuming how this works?
0
 
gregoryyoungCommented:
Temp is a reference type .. it will be passed as a reference anyway ..

passing a reference type byref actually passing a pointer to the reference (allowing you to change the initial reference).

in C/C++ the difference could be summarized between passing a char * and a char **

so ...

public void func(ref SomeClass _Foo) {
    _Foo = new SomeClass();
}

public void func1(SomeClass _Foo) {
    _Foo = new SomeClass();
}

then you call ..

SomeClass Test = new SomeClass();
SomeClass tmp = Test;
func1(Test);
Assert.AreEqual(Test, tmp); //true
func(ref Test);
Assert.AreEqual(Test, tmp); //false, the function created a new instance.


Accessing properties etc however works the same.

public void func(ref SomeClass _Foo) {
    _Foo.Bar = 13;
}

public void func1(SomeClass _Foo) {
    _Foo.Bar = 13;
}

In both instances .. the _Foo object will have its property changed to 13.

HTH

Greg
0
 
gromulAuthor Commented:
> Temp is a reference type .. it will be passed as a reference anyway ..

But when temp is created, a new object is created. If myClasses object contains 100 myClass objects, 100 temp objects will be created. And if I do something like this:

for(int i=0; i<myClasses.count; i++)
{
   func1(ref myClasses, int index);
}

no new objects are created because I'm passing myClasses as a reference. Am I correcting in reasoning that passing myClasses object as a reference is more efficient than creating temp objects, then passing them as references?
0
 
gregoryyoungCommented:
it is NOT creating a new object each time .. Only value objects pass a copy.

it is passing a reference to the existing object.


Take the following code ..

public void func1(SomeClass _Foo) {
    _Foo.Bar = 13;
}

SomeClass Test = new SomeClass();
Test.Bar = 12;
func1(Test);
Assert.AreEqual(Test.Bar, 13); //will be true as a reference of Test was passed to the function
0
 
gromulAuthor Commented:
I tested your code and you were right. In C++ it is different, right? Even complex objects can be passed by value, and only explicit references are really references?
0
 
gregoryyoungCommented:
a reference object in C# is roughly equivalent to a pointer in C/C++ when you pass a ref object it passes a pointer to a pointer in C/C++ terms ...

take the example of a char * in C ..
 
when passed a function .. I can change what is in the memory where char * points to but I can't change where the char * points to .. i.e. I can go to memory location 11111 where it points and change it from "Greg" to "Young" but it still points to the same place .. with a char ** I could change it from pointing to 11111 to point to 22222 ...

Make sense ?



0
 
gromulAuthor Commented:
Does make sense. Thank you!
0

Featured Post

[Webinar] Cloud and Mobile-First Strategy

Maybe you’ve fully adopted the cloud since the beginning. Or maybe you started with on-prem resources but are pursuing a “cloud and mobile first” strategy. Getting to that end state has its challenges. Discover how to build out a 100% cloud and mobile IT strategy in this webinar.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now