fundamental scope question

D

David

I feel like an idiot asking this but here goes: I understand the 'concept'
of scope and passing data by value and/or by reference but I am confused on
some specifics.

class example{
int i; //my global var

private void method1(int myInt){
int x; //local var
etc...
}

private void method2(int myInt){
int z; //local var
etc...
}
}

so I get that the global var i is accessible by any methods in above class
example, based on where its declared, and that the var x, for example, is
local to method1 and only accessible from method1. And beyond that, within a
method, you can have further localized variables within conditional IF
blocks or loops. I get that passing data byval passes a 'copy' of the passed
variable's data and that passing byref passes the actual memory location of
the passed variable. So, for instance, if my global var i was passed byref
to either of those methods in the example and modified, that actual global
var i would be modified directly... whereas if it was passed byval and
modified within the method that the global i var would remain untouched.

now here is where I get confused: say the var x in method1 was a reference
type like 'object'. It is declared in method1, so scope is only method1.
What if method2 was called from method1, and x is passed in. Would the x
used in method2 be the same x from method1? If I wanted it to be would x
have to be declared outside both those methods where my example global var i
is?

side bar question: does c# pass parameters by value or by reference by
default? (i think its by value except I recall something special about
strings?)
 
N

Nicholas Paldino [.NET/C# MVP]

David,

Well, i isn't global, in the sense that it is scoped to instances of the
class example. Only other members of the class can see i.

Here is an example which should help you understand the difference
between passing references by value, and by reference.

public class MyClass
{
public string Name = null;
}

public void DoSomething(MyClass myClass)
{
// myClass is passed by value. In this case, it is the reference that
is passed by value.
// You can make changes to members of the instance, but not the
reference itself. So the following
// will change the name.
myClass.Name = "Nicholas Paldino";

// However, this will not change what myClass points to. For the rest
of the method though, myClass
// will point to the new instance.
myClass = new MyClass();
}

public void DoSomething2(ref MyClass myClass)
{
// Here, myClass can be changed.
myClass = new MyClass();
}

public void Test()
{
// Create an instance of MyClass.
MyClass mc = new MyClass();

// Set the name.
mc.Name = "David";

// Store a reference for comparison later.
MyClass oldMc = mc;

// Call the first DoSomething method.
DoSomething(mc);

// At this point, mc.Name will be "Nicholas Paldino".
Console.WriteLine("mc.Name: {0}, mc.Name);

// The reference has not changed.
Console.WriteLine("mc == oldMc: {0}", Object.ReferenceEquals(mc,
oldMc));

// Now call DoSomething2.
DoSomething2(ref mc);

// The references are not the same.
Console.WriteLine("mc == oldMc after call to DoSomething2: {0}",
Object.ReferenceEquals(mc, oldMc));
}

Hope this helps.
 
G

Guest

Pl give a look at the concept of OUT Key word in C# Language Ref.

Dont get confuse with the VB's ByVal and ByRef keywords in C# Language.

For ByRef of VB, all you have do is use OUT key word in C# as mentioned below

int y;
public int DoSome(int x, out int y)
{
y=10; // will send the value out of the scope
x=11; // will distroy the value inside this method
}

HTH
 
M

Moty Michaely

I feel like an idiot asking this but here goes: I understand the 'concept'
of scope and passing data by value and/or by reference but I am confused on
some specifics.

class example{
int i; //my global var

private void method1(int myInt){
int x; //local var
etc...
}

private void method2(int myInt){
int z; //local var
etc...
}

}

so I get that the global var i is accessible by any methods in above class
example, based on where its declared, and that the var x, for example, is
local to method1 and only accessible from method1. And beyond that, within a
method, you can have further localized variables within conditional IF
blocks or loops. I get that passing data byval passes a 'copy' of the passed
variable's data and that passing byref passes the actual memory location of
the passed variable. So, for instance, if my global var i was passed byref
to either of those methods in the example and modified, that actual global
var i would be modified directly... whereas if it was passed byval and
modified within the method that the global i var would remain untouched.

now here is where I get confused: say the var x in method1 was a reference
type like 'object'. It is declared in method1, so scope is only method1.
What if method2 was called from method1, and x is passed in. Would the x
used in method2 be the same x from method1? If I wanted it to be would x
have to be declared outside both those methods where my example global var i
is?

side bar question: does c# pass parameters by value or by reference by
default? (i think its by value except I recall something special about
strings?)

David,

I'll start from the end: All primitive types are passed by value (like
int, decimal etc). string is a special case since it's passed by
reference but is immutable so you can't change it's content, only the
location it points to (you can do s="m").

Since your example functions are getting int parameters, each of the
passed parameter is passed by value.
You can use the 'ref' keyword to pass primitives by reference:
//
void method1(ref int i)
{
++i;
}

Hope this helps.
Moty
 
N

Nicholas Paldino [.NET/C# MVP]

The ref keyword is more like ByRef in VB. AFAIK, out doesn't have a
representation in VB.NET.
 
J

Jon Skeet [C# MVP]

Moty Michaely said:
I'll start from the end: All primitive types are passed by value (like
int, decimal etc). string is a special case since it's passed by
reference but is immutable so you can't change it's content, only the
location it points to (you can do s="m").

No.

a) String is a reference type to start with
b) String isn't treated differently to any other reference types
c) *All* parameters are passed by value by default

See http://pobox.com/~skeet/csharp/parameters.html
Since your example functions are getting int parameters, each of the
passed parameter is passed by value.

They *are* being passed by value, but the reason is simple: they're not
declared as ref or out.
 
P

Peter Duniho

I feel like an idiot asking this but here goes: I understand the
'concept' of scope and passing data by value and/or by reference
but I am confused on some specifics.

I recommend you look at the link Jon offered. He's done a nice write-up
on this question, as it comes up a lot. :)

For your specific questions (they should be answered by Jon's write-up,
but just in case):
now here is where I get confused: say the var x in method1 was a
reference type like 'object'. It is declared in method1, so scope is
only method1.

Keep in mind that "scope" and "lifetime" are not exactly the same thing.
They are often related, but they are two different characteristics of a
variable.

In particular, while you are correct that the scope of x is limited to
method1, and the lifetime of x is only during the time between the entry
to method1 and the exit from method1, this also means that the lifetime of
x includes any calls method1 may make to other methods.
What if method2 was called from method1, and x is passed in. Would thex
used in method2 be the same x from method1?

By default, no. However, if you use "ref" or "out", it would be the same.
If I wanted it to be would x
have to be declared outside both those methods where my example global
var i is?

No, x would not have to be declared "global" (as someone else noted, it's
not really global...it's just outside the methods, but in the class). You
can use "ref" or "out" to have the variable x in method1 actually
manipulated by some other method.

Obviously, this only works for things you pass to the method, since those
keywords only apply to parameters to methods. :)
side bar question: does c# pass parameters by value or by reference by
default? (i think its by value except I recall something special about
strings?)

Strings are not special-cased, but because they are immutable reference
types, they do in some ways behave more like value types than reference
types.

By default passing is always "by value". The "ref" and "out" keywords
cause things to be passed "by reference". Not to be confused with the
difference between "value types" and "reference types". Again, I think
Jon's write-up does a good job of outlining the various combinations of
value and reference types being passed by value and by reference. A quick
summary though:

1) pass value type by value: a copy of the value type is made and
passed to the method
example:
void method1(int x)
{
x = 5;
}
void method2()
{
int y = 4;

method1(y);
Console.WriteLine(y);
}

output:
4

2) pass reference type by value: a copy of the reference is made and
passed to the method, but it's important to note that it's the *reference*
being copied, not the object itself. So unless the reference type is
immutable (like String, for example), the object itself can be modified by
the called method (but the reference to the object cannot be)
example:
class A
{
public int i;

public A(int j)
{
i = j;
}
}
void method1(A x)
{
x.i = 5;
x = new A(6);
}
void method2()
{
A y = new A(4);

method1(y);
Console.WriteLine(y.i);
}

output:
5

3) pass value type by reference: a reference to the value type is
passed, and the called method can modify the original value type instance
example:
void method1(ref int x)
{
x = 5;
}
void method2()
{
int y = 4;

method1(ref y);
Console.WriteLine(y);
}

output:
5

4) pass reference type by reference: a reference to the reference is
made; not only can the reference type object be modifed as in case #2, but
the reference *to* that object can be modified as well.
example:
class A
{
public int i;

public A(int j)
{
i = j;
}
}
void method1(ref A x)
{
x.i = 5;
x = new A(6);
}
void method2()
{
A y = new A(4);

method1(ref y);
Console.WriteLine(y.i);
}

output:
6

Hope that helps.

Pete
 
D

David

thanks all for the great information. I'm heading to review Jon's page now
for a review of passing parameters. Below is one last question that Peter
provided the perfect setup for:

from Peter:
"Keep in mind that "scope" and "lifetime" are not exactly the same thing.
They are often related, but they are two different characteristics of a
variable.

In particular, while you are correct that the scope of x is limited to
method1, and the lifetime of x is only during the time between the entry
to method1 and the exit from method1, this also means that the lifetime of
x includes any calls method1 may make to other methods."

perfect setup for this question: scope limited to method1, lifetime from
entry to exit of method1, so what if in method1 an asynchronous method2 is
called that takes a variable that was declared in method1 as a parameter
(either a value type by ref, or a reference type by val), method1 exits
since the asnyc method2 does not block, what happens to that parameter since
its lifetime would be over? will method2, which could still be running, be
able to utilize it? does the framework realize this is happening and extend
the life of the var for method2?

I'm going to read Jon's page now... thanks again.
 
J

Jon Skeet [C# MVP]

perfect setup for this question: scope limited to method1, lifetime from
entry to exit of method1, so what if in method1 an asynchronous method2 is
called that takes a variable that was declared in method1 as a parameter
(either a value type by ref, or a reference type by val), method1 exits
since the asnyc method2 does not block, what happens to that parameter since
its lifetime would be over?

For a reference type by value, there's no problem at all - the
parameter being passed is just passed by value, i.e. a copy of the
reference is made; the variable itself isn't involved.

For parameters passed by reference, it's a very interesting question
which I hadn't thought of before.

I had to consult the docs, and what happens is that the parameter is
effectively passed by value instead of by reference, but the value at
the end of the asynchronous call is available when you call EndInvoke
instead.

It's nice to learn something new...
 
P

Peter Duniho

[...]
perfect setup for this question: scope limited to method1, lifetime from
entry to exit of method1, so what if in method1 an asynchronous method2
is
called that takes a variable that was declared in method1 as a parameter
(either a value type by ref, or a reference type by val), method1 exits
since the asnyc method2 does not block, what happens to that parameter
since its lifetime would be over?

Good question. Certainly, doing that would be a bad thing. It would be
unusual (but not impossible) to set up that scenario in the first place,
but if you did, then indeed if the lifetime of the variable does not span
the time during which method2 executes, the variable would be "gone"
before the method2 tried to use it. That's bad. :)

However, I cobbled up some code (see below) to see what .NET would
actually do, and it didn't complain at all. I have no idea what got set
to "5" in my method2(), but it definitely wasn't the variable "j" in my
method1() (though it might be the storage used by that local
variable...see comments below).

Maybe someone who actually knows can answer that part of the question.
Regardless of what .NET does though, you should not do this.
will method2, which could still be running, be
able to utilize it? does the framework realize this is happening and
extend the life of the var for method2?

As I mention above, I don't know what .NET is actually doing. In my test
code, executing a second method with the same local variables as the first
method doesn't change the value of the ref parameter. From that, I
presume that .NET's garbage collection has actually taken the local
variable, noted that it's still referenced, and the lifetime of the
variable extends outside of the method's lifetime.

Regardless of what .NET is doing, however, I submit that it's probably not
a good idea to pass things by reference unless the calling code is going
to stick around long enough to find out what happened to them. :)

Pete

here's my test code...note the use of waitable events to ensure that each
thread executes the statements in the order I want them to. Real-world
code might or might not have something similar, depending on what it's
actually doing.

class Program
{
static AutoResetEvent are1 = new AutoResetEvent(false);
static AutoResetEvent are2 = new AutoResetEvent(false);

delegate void RefParmHandler(ref int i);

static void method2(ref int i)
{
are1.WaitOne();
i = 5;
are2.Set();
}

static void method1()
{
int j = 4;
RefParmHandler rpf = new RefParmHandler(method2);

rpf.BeginInvoke(ref j, null, null);
}

static void method3()
{
int k = 3;
}

static void Main(string[] args)
{
method1();
method3();
are1.Set();
are2.WaitOne();
}
}
 
J

Jon Skeet [C# MVP]

Maybe someone who actually knows can answer that part of the question.
Regardless of what .NET does though, you should not do this.

Well, the behaviour is well-defined (by the CLI spec). It's very
obscure though - if I found a case where I thought it was really,
really the nicest way of doing things, I'd be putting big scary
comments explaining what was going on. I try to avoid writing "clever"
code which needs explanation.

Regardless of what .NET is doing, however, I submit that it's probably not
a good idea to pass things by reference unless the calling code is going
to stick around long enough to find out what happened to them. :)

In this case, other code can still find out what happened using
EndInvoke.
 
P

Peter Duniho

Well, the behaviour is well-defined (by the CLI spec). It's very
obscure though - if I found a case where I thought it was really,
really the nicest way of doing things, I'd be putting big scary
comments explaining what was going on. I try to avoid writing "clever"
code which needs explanation.

Yes, I agree that given it's a well-defined behavior (I didn't realize
this when I wrote my previous post), I should back off the absolute
admonishment to not do it. However, I still think it's almost always a
sign of poor design. :) I'd love to see an example of code where it
could be strongly argued that it "was really, really the nicest way of
doing things". I'm having a hard time imagining a place where a "ref"
parameter is clearly superior to using some less-opaque means (like using
a reference type) :)

Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
Yes, I agree that given it's a well-defined behavior (I didn't realize
this when I wrote my previous post), I should back off the absolute
admonishment to not do it. However, I still think it's almost always a
sign of poor design. :) I'd love to see an example of code where it
could be strongly argued that it "was really, really the nicest way of
doing things". I'm having a hard time imagining a place where a "ref"
parameter is clearly superior to using some less-opaque means (like using
a reference type) :)

Agreed. One possible example would be where you already *had* the
implementation of what you wanted to do as a method, and needed to use
that implementation as a delegate action. You may not be able to change
the signature of the method (it could be part of an interface
implementation or somesuch). You could create a new method which called
the existing one, admittedly, but that would be irritating too.

I wonder how many people have ever used this? I *hope* it's vanishingly
small.

(Personally I try to avoid out/ref parameters anyway, aside from
TryParse type operations.)
 
?

=?ISO-8859-1?Q?G=F6ran_Andersson?=

David said:
now here is where I get confused: say the var x in method1 was a reference
type like 'object'. It is declared in method1, so scope is only method1.

The reference variable has that scope, but the object itself doesn't
have any scope at all.

The reference variable is allocated on the stack and lives as long as
the method is running. The object is allocated on the heap and lives
until garbage collected.
What if method2 was called from method1, and x is passed in. Would the x
used in method2 be the same x from method1?

Yes, the object used would be the same.
If I wanted it to be would x
have to be declared outside both those methods where my example global var i
is?

No. When you pass the reference to another method, that reference
becomes a new local variable in that method, with it's own scope. You
have two reference variables with different scopes that reference the
same object, and as I said the object itself doesn't have any scope at all.
 
D

David

thanks Goran. After reading Peter and Jon's information (including Jon's web
pages) I now realize this. And what you have answered here was exactly my
point of confusion.

thanks again all.
 
P

Peter Duniho

[...]
For parameters passed by reference, it's a very interesting question
which I hadn't thought of before.

I had to consult the docs, and what happens is that the parameter is
effectively passed by value instead of by reference, but the value at
the end of the asynchronous call is available when you call EndInvoke
instead.

Can you provide a link to the docs that describe that behavior? I'd love
to see what the specific syntax is for getting at the ref parameter.

Thanks!
Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
[...]
For parameters passed by reference, it's a very interesting question
which I hadn't thought of before.

I had to consult the docs, and what happens is that the parameter is
effectively passed by value instead of by reference, but the value at
the end of the asynchronous call is available when you call EndInvoke
instead.

Can you provide a link to the docs that describe that behavior? I'd love
to see what the specific syntax is for getting at the ref parameter.

The easiest way to find the syntax is to compile a delegate type with a
ref parameter, and use ildasm or reflector to look at the signature of
EndInvoke. For instance:

delegate void Foo(ref int x, out int y, int z);

creates an EndInvoke method of:

void EndInvoke (ref x, out y, IAsyncResult result)

As for the documentation of this - it's briefly described in the ECMA-
335 spec, section 14.6.3.2, but trying it gives a more concrete feel :)
 
P

Peter Duniho

[...]
As for the documentation of this - it's briefly described in the ECMA-
335 spec, section 14.6.3.2, but trying it gives a more concrete feel :)

Thanks. I think it's unfortunate that using "ildasm or reflector" is the
easiest way to answer my question. IMHO, the docs for EndInvoke ought to
include this information. :( The concept is documented on a more general
"calling synchronous methods asynchronously" page
(http://msdn2.microsoft.com/en-us/library/2e08f6yc.aspx), but it's a
pretty subtle reference.

Ah well...at least it's possible. Documentation can be improved. :)

Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
[...]
As for the documentation of this - it's briefly described in the ECMA-
335 spec, section 14.6.3.2, but trying it gives a more concrete feel :)

Thanks. I think it's unfortunate that using "ildasm or reflector" is the
easiest way to answer my question. IMHO, the docs for EndInvoke ought to
include this information. :(

The trouble is that because EndInvoke is created separately for every
delegate, there *is* no Delegate.EndInvoke to have good documentation.
The concept is documented on a more general
"calling synchronous methods asynchronously" page
(http://msdn2.microsoft.com/en-us/library/2e08f6yc.aspx), but it's a
pretty subtle reference.

Ah well...at least it's possible. Documentation can be improved. :)

Indeed. I suspect it's a *very* rare case...
 
P

Peter Duniho

The trouble is that because EndInvoke is created separately for every
delegate, there *is* no Delegate.EndInvoke to have good documentation.

Yet another "Control.Invoke vs Delegate.Invoke" issue?

There *is* a doc page for Control.EndInvoke(). I naively have assumed
that the same concepts apply to both, and that since you can call
EndInvoke on a delegate and get "ref" and "out" parameters, you can do the
same with Control instances.

But now that you point out the lack of a Delegate.EndInvoke() doc page, I
note that Control.EndInvoke() is a specific method attached to the Control
class, rather than being auto-generated according to the delegate being
used (just as Control.Invoke() or Control.BeginInvoke() is a specific
method, and requires passing of parameters as an array in a single
parameter, rather than using the signature provided for by the delegate
being invoked).

Nonetheless, just because EndInvoke() is different for each delegate,
according to its signature, I don't see why there should not be a doc page
for it. After all, I got to the page that describes the
Delegate.EndInvoke() syntax from the Control.EndInvoke() doc page.
Obviously there's some room for less-concrete contexts for documentation.
Indeed. I suspect it's a *very* rare case...

True. And as I think I've mentioned previously, I think that's a good
thing. I can't imagine many, if any, scenarios in which passing "ref" or
"out" to an asynchronously-invoked delegate could be shown to be good
design. Still, one of my biggest complaints about programming has always
been, and continues to be, the poor documentation. I think MSDN has made
great strides beyond what I used to have to deal with, but there are still
places it could be *lots* better.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top