Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


C# delegates blow my mind


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
19 replies to this topic

#1 deadlydog   Members   -  Reputation: 170

Like
0Likes
Like

Posted 14 May 2008 - 04:36 AM

I noticed that C# does not have an inline keyword for functions. Seeing this, I wanted to see how fast a function call was, and if it really made much of a difference putting the code inline VS putting it in a function call. I did a test executing the same code with 4 different approaches. The code executed in each approach is 9 operations (add and subtract). The 4 approaches are: 1 - putting the code all inline 2 - putting the code in a function and calling the function 3 - using a delegate to call the function 4 - putting each one of the 9 operations in their own function, and using a multicast delegate to call the 9 functions My goal was to see how many times the code could be executed in a specified length of time. I specify how long the approaches should run for and how many times they should be ran (the same values are used for all four approaches). I then take the average number of times the code was executed for each approach and compare them. I was expecting approach 1 to be the fastest, approaches 2 and 3 to be about the same, but much slower than approach 1, and approach 4 to be very slow. To my amazement appraoches 1, 2 and 3 all perform about the same, and approach 4 suffers maybe a 1% performance hit, if that. This was not the results that I was expecting. I have done the test many times, specifying different amounts of time the approaches should run for (1 - 60 seconds), and the number of times they should be ran (1 - 20, then take the average), and I get consistent results. Below is my code; I want to make sure that there is nothing I am overlooking. I use a high resolution Stopwatch to control how long each approach runs for, and I randomly pick the order that each of the 4 approaches is called in. Also, for the function calls I pass a class object as the single parameter, which is the object to update, and I make sure I'm not running any other applications when I do the test. If you can spot a potential problem in my code (or something that I am not considering) let me know. I just found this very interesting and thought I would share it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace Delegate_Speed_Test
{
    public partial class Form1 : Form
    {
        // Class to hold data to update
        class CParticle
        {
            public float f1;
            public float f2;
            public float f3;
            public int i4;
            public int i5;
            public int i6;
            public string s7;
            public string s8;
            public long l9;

            public CParticle()
            {
                f1 = f2 = f3 = 0.0f;
                i4 = i5 = i6 = 0;
                s7 = s8 = "";
                l9 = 0;
            }
        }

        // Define the Delegate function structure
        delegate void UpdateDelegate(CParticle cParticle);

        // Variables used to calculate the average number of times executed
        long mlNumberOfTimesRanInline = 0;
        long mlNumberOfTimesRanFunction = 0;
        long mlNumberOfTimesRanDelegate = 0;
        long mlNumberOfTimesRanMulticastDelegate = 0;
        
        Random mcRandom = new Random();
                

        public Form1()
        {
            InitializeComponent();

            UpdateShownTimeNeeded();
        }

        // Show how long the test will take to run using the given duration and number of times to run
        private void UpdateShownTimeNeeded()
        {
            float fTimeInSeconds = (float)(numericNumberOfTimesToRun.Value * numericLengthOfTimeToRun.Value * 4);
            float fTimeInMinutes = fTimeInSeconds / 60.0f;

            labelTimeNeededToRunInSeconds.Text = fTimeInSeconds.ToString();
            labelTimeNeededToRunInMinutes.Text = fTimeInMinutes.ToString();
        }

        // Start the test
        private void buttonStart_Click(object sender, EventArgs e)
        {
            // Reset the number of times each has ran
            mlNumberOfTimesRanInline = 0;
            mlNumberOfTimesRanFunction = 0;
            mlNumberOfTimesRanDelegate = 0;
            mlNumberOfTimesRanMulticastDelegate = 0;

            int iIndex = 0;
            for (iIndex = 0; iIndex < numericNumberOfTimesToRun.Value; iIndex++)
            {
                // Call the functions in a random order
                switch ((int)mcRandom.Next(0, 10))
                {
                    default:
                    case 0:
                        Inline();
                        Function();
                        Delegate();
                        MulticastDelegate();
                    break;

                    case 1:
                        Function();
                        Delegate();
                        MulticastDelegate();
                        Inline();
                    break;

                    case 2:
                        Delegate();
                        MulticastDelegate();
                        Inline();
                        Function();
                    break;

                    case 3:
                        MulticastDelegate();
                        Inline();
                        Function();
                        Delegate();
                    break;

                    case 4:
                        MulticastDelegate();
                        Delegate();
                        Function();
                        Inline();
                    break;

                    case 5:
                        Inline();
                        MulticastDelegate();
                        Delegate();
                        Function();
                    break;

                    case 6:
                        Function();
                        Inline();
                        MulticastDelegate();
                        Delegate();
                    break;

                    case 7:
                        Delegate();
                        Function();
                        Inline();
                        MulticastDelegate();
                        break;

                    case 8:
                        Inline();
                        Delegate();
                        Function();
                        MulticastDelegate();
                    break;

                    case 9:
                        MulticastDelegate();
                        Function();
                        Delegate();
                        Inline();
                    break;
                }
            }

            // Display Inline Info
            double dAverageTimesRanInline = (double)mlNumberOfTimesRanInline / (double)numericNumberOfTimesToRun.Value;
            labelNumberOfTimesInline.Text = dAverageTimesRanInline.ToString("#.###");
            labelNumberOfTimesInlinePercent.Text = "100.0%";

            // Display Function Info
            double dAverageTimesRanFunction = (double)mlNumberOfTimesRanFunction / (double)numericNumberOfTimesToRun.Value;
            labelNumberOfTimesFunction.Text = dAverageTimesRanFunction.ToString("#.###");
            float fPercent = (float)((dAverageTimesRanFunction / dAverageTimesRanInline) * 100.0f);
            labelNumberOfTimesFunctionPercent.Text = fPercent.ToString() + "%";

            // Display Delegate Info
            double dAverageTimesRanDelegate = (double)mlNumberOfTimesRanDelegate / (double)numericNumberOfTimesToRun.Value;
            labelNumberOfTimesDelegate.Text = dAverageTimesRanDelegate.ToString("#.###");
            fPercent = (float)((dAverageTimesRanDelegate / dAverageTimesRanInline) * 100.0f);
            labelNumberOfTimesDelegatePercent.Text = fPercent.ToString() + "%";

            // Display MulticastDelegate Info
            double dAverageTimesRanMulitcastDelegate = (double)mlNumberOfTimesRanMulticastDelegate / (double)numericNumberOfTimesToRun.Value;
            labelNumberOfTimesMulticastDelegate.Text = dAverageTimesRanMulitcastDelegate.ToString("#.###");
            fPercent = (float)((dAverageTimesRanMulitcastDelegate / dAverageTimesRanInline) * 100.0f);
            labelNumberOfTimesMulticastDelegatePercent.Text = fPercent.ToString() + "%";
        }

        // Approach 1 - Code inline
        private void Inline()
        {
            long lNumberOfTimesExecuted = 0;
            long lAmountOfTimeToRunFor = (long)(numericLengthOfTimeToRun.Value * 1000);

            CParticle cParticle = new CParticle();
            Stopwatch cStopwatch = new Stopwatch();
            cStopwatch.Start();

            while (cStopwatch.ElapsedMilliseconds < lAmountOfTimeToRunFor)
            {
                lNumberOfTimesExecuted++;

                cParticle.f1 += 1.5f;
                cParticle.f2 = 15.567f;
                cParticle.f3 -= 0.0001f;
                cParticle.i4 += 3;
                cParticle.i5 = 23345;
                cParticle.i6 -= 7;
                cParticle.s7 = "Hello";
                cParticle.s8 += "A";
                cParticle.l9 += 123;
            }

            mlNumberOfTimesRanInline += lNumberOfTimesExecuted;
        }

        // Approach 2 - Code in a function call
        private void Function()
        {
            long lNumberOfTimesExecuted = 0;
            long lAmountOfTimeToRunFor = (long)(numericLengthOfTimeToRun.Value * 1000);

            CParticle cParticle = new CParticle();
            Stopwatch cStopwatch = new Stopwatch();
            cStopwatch.Start();

            while (cStopwatch.ElapsedMilliseconds < lAmountOfTimeToRunFor)
            {
                lNumberOfTimesExecuted++;

                Update(cParticle);
            }

            mlNumberOfTimesRanFunction += lNumberOfTimesExecuted;
        }

        // Approach 3 - Code in a function call, called from a delegate
        private void Delegate()
        {
            long lNumberOfTimesExecuted = 0;
            long lAmountOfTimeToRunFor = (long)(numericLengthOfTimeToRun.Value * 1000);
            
            UpdateDelegate MyDelegate = new UpdateDelegate(Update);

            CParticle cParticle = new CParticle();
            Stopwatch cStopwatch = new Stopwatch();
            cStopwatch.Start();

            while (cStopwatch.ElapsedMilliseconds < lAmountOfTimeToRunFor)
            {
                lNumberOfTimesExecuted++;

                MyDelegate(cParticle);
            }

            mlNumberOfTimesRanDelegate += lNumberOfTimesExecuted;
        }

        // Approach 4 - Code in several function calls, each called from a multicast delegate
        private void MulticastDelegate()
        {
            long lNumberOfTimesExecuted = 0;
            long lAmountOfTimeToRunFor = (long)(numericLengthOfTimeToRun.Value * 1000);
            
            UpdateDelegate MyDelegate = null;
            MyDelegate += new UpdateDelegate(Update1);
            MyDelegate += new UpdateDelegate(Update2);
            MyDelegate += new UpdateDelegate(Update3);
            MyDelegate += new UpdateDelegate(Update4);
            MyDelegate += new UpdateDelegate(Update5);
            MyDelegate += new UpdateDelegate(Update6);
            MyDelegate += new UpdateDelegate(Update7);
            MyDelegate += new UpdateDelegate(Update8);
            MyDelegate += new UpdateDelegate(Update9);

            CParticle cParticle = new CParticle();
            Stopwatch cStopwatch = new Stopwatch();
            cStopwatch.Start();

            while (cStopwatch.ElapsedMilliseconds < lAmountOfTimeToRunFor)
            {
                lNumberOfTimesExecuted++;

                MyDelegate(cParticle);
            }

            mlNumberOfTimesRanMulticastDelegate += lNumberOfTimesExecuted;
        }

        // Function containing code to run
        private void Update(CParticle cParticle)
        {
            cParticle.f1 += 1.5f;
            cParticle.f2 = 15.567f;
            cParticle.f3 -= 0.0001f;
            cParticle.i4 += 3;
            cParticle.i5 = 23345;
            cParticle.i6 -= 7;
            cParticle.s7 = "Hello";
            cParticle.s8 += "A";
            cParticle.l9 += 123;
        }

        // Functions containing code to run (spread across several functions)
        private void Update1(CParticle cParticle)
        {
            cParticle.f1 += 1.5f;
        }

        private void Update2(CParticle cParticle)
        {
            cParticle.f2 = 15.567f;
        }

        private void Update3(CParticle cParticle)
        {
            cParticle.f3 -= 0.0001f;
        }

        private void Update4(CParticle cParticle)
        {
            cParticle.i4 += 3;
        }

        private void Update5(CParticle cParticle)
        {
            cParticle.i5 = 23345;
        }

        private void Update6(CParticle cParticle)
        {
            cParticle.i6 -= 7;
        }

        private void Update7(CParticle cParticle)
        {
            cParticle.s7 = "Hello";
        }

        private void Update8(CParticle cParticle)
        {
            cParticle.s8 += "A";
        }

        private void Update9(CParticle cParticle)
        {
            cParticle.l9 += 123;
        }


        // If the user changed how long each approach should run for
        private void numericLengthOfTimeToRun_ValueChanged(object sender, EventArgs e)
        {
            UpdateShownTimeNeeded();
        }

        // If the user changed how many times each approach should be run
        private void numericNumberOfTimesToRun_ValueChanged(object sender, EventArgs e)
        {
            UpdateShownTimeNeeded();
        }
    }
}



-Dan- Can't never could do anything | DansKingdom.com | Dynamic Particle System Framework for XNA

Sponsor:

#2 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 14 May 2008 - 04:40 AM

Quote:
Original post by deadlydog
I noticed that C# does not have an inline keyword for functions.

Most C++ compilers ignore the "inline" keyword when determining whether or not to inline a function. Why should C# be any different?

The real issue, though, is JIT compiling, which is ideal for dynamic dispatch situations in which the same function is always chosen. JIT engines optimize for this situation (as do C++ compilers, in some circumstances), meaning that the difference between a delegate call and explicitly inlined code is usually just a pointer comparison.

EDIT: Actually, now that I look at it, the .NET runtime doesn't seem to do this.

[Edited by - Sneftel on May 14, 2008 11:40:17 AM]

#3 Washu   Senior Moderators   -  Reputation: 6374

Like
0Likes
Like

Posted 14 May 2008 - 04:43 AM

You should perhaps read my blog posts on this very subject. Inlining in .Net is actually quite different than perhaps what you're used to.

#4 deadlydog   Members   -  Reputation: 170

Like
0Likes
Like

Posted 14 May 2008 - 04:45 AM

Quote:
Original post by Sneftel
Quote:
Original post by deadlydog
I noticed that C# does not have an inline keyword for functions.

Most C++ compilers ignore the "inline" keyword when determining whether or not to inline a function. Why should C# be any different?

Yes, I know. the "inline" keyword is more of a hint to the compiler, rather than a strict rule it must follow. I was just blown away by how fast the function calls are; for example, in my test both the inline approach and the function call approach can execute the code 22000 times per second, even though the function call approach is making 22000 more function calls than the inline approach. So it seems that calling a function takes virtually no time at all. However, as I pointed out above, this is when passing a single parameter to the function, and it's passed by reference. If you were passing 10 parameters by value to the function, that may slow things down a bit.......hmmmm, I think I'll try that out.
-Dan- Can't never could do anything | DansKingdom.com | Dynamic Particle System Framework for XNA

#5 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 14 May 2008 - 04:51 AM

Quote:
Original post by deadlydog
So it seems that calling a function takes virtually no time at all.

This is definitely true when the function call is in a correctly predicted branch. Modern processors see the branch coming up and prefetch the branched-to instructions, meaning that execution continues similarly to if there was no branch at all.

#6 -MadHatter   Members   -  Reputation: 122

Like
0Likes
Like

Posted 14 May 2008 - 05:17 AM

reference types are passed by reference. value types are passed by value. anything declared as a struct is a value type, while things declared class are reference types.

default behavior for reference type passing is "sort of" by reference but not exactly. if you assign your parameter value to a new instance, it creates a new reference and the value of the variable passed into the method remains unchanged. modifying members of the reference type parameter will work like a by reference call.

the following method is how you really pass by reference in C# for both reference and value types.

public void Foo(ref MyObject mo) {
// ...
}


#7 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 14 May 2008 - 05:29 AM

Quote:
Original post by -MadHatter
reference types are passed by reference. value types are passed by value. anything declared as a struct is a value type, while things declared class are reference types.

You seem to be confusing reference types with reference passing. C# passes both value and reference types by value, unless the ref keyword is given. The remainder of your post shows that you understand the practical upshot of C#'s system, but you should get your terminology straight. There's nothing "sort of" about C#'s evaluation strategy. ref variables are passed by reference; everything else, value type or reference type, is passed by value.

#8 deadlydog   Members   -  Reputation: 170

Like
0Likes
Like

Posted 14 May 2008 - 05:44 AM

Quote:
Original post by Sneftel
Quote:
Original post by -MadHatter
reference types are passed by reference. value types are passed by value. anything declared as a struct is a value type, while things declared class are reference types.

You seem to be confusing reference types with reference passing. C# passes both value and reference types by value, unless the ref keyword is given. The remainder of your post shows that you understand the practical upshot of C#'s system, but you should get your terminology straight. There's nothing "sort of" about C#'s evaluation strategy. ref variables are passed by reference; everything else, value type or reference type, is passed by value.

So does this mean that it's faster to pass my class object with the ref keyword (since you say this is the only way to make it actually pass the reference (i.e. pointer in c++ speak) of the object, not a copy of it)?
Thanks
-Dan- Can't never could do anything | DansKingdom.com | Dynamic Particle System Framework for XNA

#9 Washu   Senior Moderators   -  Reputation: 6374

Like
0Likes
Like

Posted 14 May 2008 - 05:44 AM

Quote:
Original post by Sneftel
Quote:
Original post by -MadHatter
reference types are passed by reference. value types are passed by value. anything declared as a struct is a value type, while things declared class are reference types.

You seem to be confusing reference types with reference passing. C# passes both value and reference types by value, unless the ref keyword is given. The remainder of your post shows that you understand the practical upshot of C#'s system, but you should get your terminology straight. There's nothing "sort of" about C#'s evaluation strategy. ref variables are passed by reference; everything else, value type or reference type, is passed by value.

Heh, that's one thing that's so much easier to explain using C++ :D.


#10 DevFred   Members   -  Reputation: 836

Like
0Likes
Like

Posted 14 May 2008 - 05:48 AM

Quote:
Original post by deadlydog
since you say this is the only way to make it actually pass the reference (i.e. pointer in c++ speak) of the object, not a copy of it

He didn't say that at all. If you pass a reference type, the reference gets passed by value, NOT THE OBJECT. You only need to pass reference types by reference if you want to change the reference (NOT THE OBJECT) inside of the function.

#11 TheTroll   Members   -  Reputation: 883

Like
0Likes
Like

Posted 14 May 2008 - 06:28 AM

Quote:
Original post by Sneftel
Quote:
Original post by -MadHatter
reference types are passed by reference. value types are passed by value. anything declared as a struct is a value type, while things declared class are reference types.

You seem to be confusing reference types with reference passing. C# passes both value and reference types by value, unless the ref keyword is given. The remainder of your post shows that you understand the practical upshot of C#'s system, but you should get your terminology straight. There's nothing "sort of" about C#'s evaluation strategy. ref variables are passed by reference; everything else, value type or reference type, is passed by value.


Think I need to clear something up here before people get confused.

First of all we will go over some terms to make sure we are all on the same page.
reference type and value type - these are the two base types, value types are structs and other base types such as int, float, ect. When you use a value type a copy of the information is made and that is used. A reference type (classes) are used by reference, so when you use them the reference is passed around not the data itself.

reference parameters vs. value parameters. Unless you use the 'ref' keyword, the parameter for a method will be a value parameter, so the parameter will be passed by value.

So what does this mean to us? Well that is where it gets a little strange.

take the fallowing

DoSomething(myClass a);

That is a value parameter, so we are passing it by value but what does it really mean? It means that we are coping the data. So what data gets copied? The reference to a gets copied and passed in. So why is this important? Let me show you.

void DoSomething(myClass a)
{
a = new myClass();
a.Text = "Did something";
}

So we do this.

myClass first = new myClass();
first.Text = "Some text";

Now we call the function.

DoSomething(first);

What will be the value of first.Text when we are done? It will still be "Some text";

Now we call the functions using a function call like so; void DoSomething(ref myClass a);

What will the value of first.Text be when you are done? "Did something".

So why is that? Because in this case we are passing the acutal reference to first and not just a copy. In the first case we changed the reference and so we were no longer referencing 'first', in the second one we were always referencing 'first'.

I really hope that didn't confuse anyone. By value of a reference is just a copy of the reference and doesn't really matter unless you change the reference.

theTroll




#12 DevFred   Members   -  Reputation: 836

Like
0Likes
Like

Posted 14 May 2008 - 06:52 AM

Quote:
Original post by TheTroll
Because in this case we are passing the acutal reference to first and not just a copy.

No, it's because we pass the reference by reference.

#13 Structural   Members   -  Reputation: 328

Like
0Likes
Like

Posted 14 May 2008 - 06:58 AM

Quote:
Original post by Washu
You should perhaps read my blog posts on this very subject. Inlining in .Net is actually quite different than perhaps what you're used to.


Now that was an interesting read. One thing popped in my mind though. In some "dumb" compilers you can optimize looping over an array sequentially by using running pointers:

for (int i = 0; i < count; i++)
{
x += array[i]; // array[i]: extra instructions for calculating the memory address of the value
}

can be optimized into:

int* ptr = array;
int* end = array + count;
while (ptr < end)
{
x += *ptr;
ptr++;
}

In an embedded audio processing project I did recently this simple optimization resulted in 10-20% fewer instructions for functions that were mainly made up of such loops.
Note though that the processing was relatively simple and was mainly made up of loops like these. The things that were done inside the loop every iteration was not much.


But what I was wondering is if the JIT will pick up on optimizations like these or if the "foreach" keyword can play a role here? A search on "foreach vs for loops" does not turn up anything that indicates this though, but I can't help but wonder.

Now, I do realize that for games this is less of an issue as the heavy number crunching does not depend on looping over arrays, but for video and audio processing this is more of an issue where you are constantly looping over buffers.

#14 TheTroll   Members   -  Reputation: 883

Like
0Likes
Like

Posted 14 May 2008 - 07:07 AM

Quote:
Original post by DevFred
Quote:
Original post by TheTroll
Because in this case we are passing the acutal reference to first and not just a copy.

No, it's because we pass the reference by reference.


No, it works exactly as I described. Why don't you test it and you will be a bit shocked.

This exact thing bit me hard when I first started working in C#.

theTroll

#15 SamLowry   Members   -  Reputation: 1714

Like
0Likes
Like

Posted 14 May 2008 - 08:24 AM

Quote:
Original post by TheTroll
Quote:
Original post by DevFred
Quote:
Original post by TheTroll
Because in this case we are passing the acutal reference to first and not just a copy.

No, it's because we pass the reference by reference.


No, it works exactly as I described. Why don't you test it and you will be a bit shocked.

This exact thing bit me hard when I first started working in C#.

theTroll


No, it's because the reference is passed by reference.

I'm not saying it doesn't behave as you described, it's your explanation that's not correct. But it's possible that in the end, it's just a terminology issue.
But what is certain, is that it does work with a reference-to-a-reference-to-an-object.



#16 TheTroll   Members   -  Reputation: 883

Like
0Likes
Like

Posted 14 May 2008 - 08:36 AM

No, the reference is passed by-value, unless you specify 'ref'.

So you have a copy of the reference, not the reference. Now in most cases this does not matter, it works the same. The exception is if you try to create a new object based on that reference. Because it is a copy of the reference and not the original reference when you do a new you do create a new object, but it's reference is assigned to the copy of the reference not to the original one. So then you will loose what you just created.

This is covered under parameters in the C# Standard.

"12.1.4 Value parameters
A parameter declared without a ref or out modifier is a value parameter.
A value parameter comes into existence upon invocation of the function member (method, instance
constructor, accessor, or operator) to which the parameter belongs, and is initialized with the value of the
argument given in the invocation. A value parameter ceases to exist upon return of the function member
(except when the value parameter is captured by an anonymous method (§14.5.15.3.1) or the function
member body is an iterator block (§26))."

theTroll

P. S. Just for some mucilaginous information. DoSomething(myClass a) and DoSomething(ref myClass a) are different in relation to function overloading. So both would be allowed without giving you an error. Makes sense just never thought of it before.

#17 DevFred   Members   -  Reputation: 836

Like
0Likes
Like

Posted 14 May 2008 - 08:46 AM

Quote:
Original post by TheTroll
Quote:
Original post by DevFred
Quote:
Original post by TheTroll
Because in this case we are passing the acutal reference to first and not just a copy.

No, it's because we pass the reference by reference.

No, it works exactly as I described. Why don't you test it and you will be a bit shocked.

It does work as you described, and I never doubted that.

All I'm saying is that your terminology is a bit fuzzy. The reason it works as described is because if you use the ref keyword, the reference gets passed by reference instead of by value. This does not make the reference more "actual".

#18 TheTroll   Members   -  Reputation: 883

Like
0Likes
Like

Posted 14 May 2008 - 08:50 AM

With all the cold medication I am on right now, I am a bit surprised I was not talking about pink bunnies. Also typed that holding a 4 month old that is sick also. So yeah, the terminology could have been much better.

Just didn't wanted to try to make sure that folks understood there is a difference between passing by-reference and passing a reference by-value.

theTroll

#19 DevFred   Members   -  Reputation: 836

Like
0Likes
Like

Posted 14 May 2008 - 09:06 AM

Quote:
Original post by TheTroll
there is a difference between passing by-reference and passing a reference by-value.

That is the gist of it, yes.

The problem is that in C# and Java, pointers were (restricted and) renamed to "references", so there are certain types that consist of values called "references". Since these references are values, expressions can yield references, you can store references in variables etc.

In C++ we have a clear distinction between pointers and references. In C++, pointers are values (addresses of objects*), whereas references are not. A reference variable is just another name for some object, there are no references to references (because a reference is not an object) etc.

References were introduced in C++ to support call-by-reference semantics in operator overloading (so user-defined types could be used just as built-in types). The concepts "reference" and "call-by-reference" are closely related in C++.

In Java there is only call-by-value (primitive types are passed by value and reference types are passed by value), and there are no user-defined value types (classes always define reference types). So the term "reference" always means "reference to an object", which is a value.

In C#, there are user-defined reference types (classes) and user-defined value types (structs), and there is call-by-value and call-by-reference. You can
- pass value types by value (the object is copied)
- pass value types by reference (the original object is used)
- pass reference types by value (the reference is copied)
- pass reference types by reference (the original reference is used)

So you have to be very carefully when you talk about the word "reference" in C#. Do you mean the reference to an instance of a class (a value), or do you mean "call-by-reference" (a parameter passing mechanism involving the keyword ref)?

* In C++, an object is something that consumes memory and has an address. Don't confuse this with the term "object" in "OOP". I bet Zahlman can come up with a better definition though ;)

[Edited by - DevFred on May 14, 2008 3:06:21 PM]

#20 -MadHatter   Members   -  Reputation: 122

Like
0Likes
Like

Posted 27 May 2008 - 04:19 AM

unlike java, you can still use normal pointers in .NET, but yea, they renamed managed pointers to handles in .net.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS