42

I'm working with the ref and don't understand clearly "Is it like a pointer as in C/C++ or it's like a reference in C++?"

Why did I ask such a weak question as you thought for a moment? Because, when I'm reading C#/.NET books, msdn or talking to C# developers I'm becoming confused by the following reasons:

  • C# developers suggest NOT to use ref in the arguments of a function, e.g. ...(ref Type someObject) doesn't smell good for them and they suggest ...(Type someObject), I really don't understand clearly this suggestion. The reasons I heard: better to work with the copy of object, then use it as a return value, not to corrupt memory by a reference etc... Often I hear such explanation about DB connection objects. As on my plain C/C++ experience, I really don't understand why to use a reference is a bad stuff in C#? I control the life of object and its memory allocations/re-allocations etc... I read in books and forums only advises it's bad, because you can corrupt your connection and cause a memory leak by a reference lose, so I control the life of object, I may control manually what I really want, so why is it bad?
  • Nowadays reading different books and talk to different people, I don't clearly understand is ref a pointer (*) or a reference like in C++ by & ? As I remember pointers in C/C++ always do allocate a space with a size of void* type - 4 bytes (the valid size depends on architecture), where hosts an address to a structure or variable. In C++ by passing a reference & there is no new allocations from the heap/stack and you work with already defined objects in memory space and there is no sub-allocating memory for a pointer externally like in plain C. So what's the ref in C#? Does .NET VM handle it like a pointer in plain C/C++ and its GC allocates temporary space for a pointer or it does a work like reference in C++? Does ref work only with a managed types correctly or for value types like bool, int it's better to switch an unsafe code and pass through a pointer in unmanaged style?
Secret
  • 2,627
  • 7
  • 32
  • 46
  • 2
    http://stackoverflow.com/questions/57483/what-are-the-differences-between-pointer-variable-and-reference-variable-in-c?rq=1 – Meds Apr 25 '13 at 06:09
  • @SamMayback and what did I say not correctly that you've posted a link to this topic? – Secret Apr 25 '13 at 06:11
  • 1
    Pleas ehave look on it http://stackoverflow.com/questions/2146202/difference-between-pointer-in-c-and-reference-type-in-c-sharp – Arpit Kulsreshtha Apr 25 '13 at 06:16
  • @ArpitKumar Maybe I'm not right by your meaning, but I think, that mine question does require more detailed info and as the main answer by you posted link I can highlight words about automatic garbage collections by using `ref`, but no words about comparison between C++ and C# techniques (except Jorge Córdoba answer, but seems to be he said not correct information by the comment at his post) – Secret Apr 25 '13 at 06:22
  • 1
    Interesting question. I use pointers/references in C every day and also used ref in C# before but never really thought about similarities in detail. As a side note, pointer size is of course platform dependent, not always 4 byte as you stated. – Rev Apr 25 '13 at 06:25
  • @Rev1.0 yeah, you are right about platform depends, but I told about default value ;) – Secret Apr 25 '13 at 06:27
  • 7
    Since you are coming from C++, you're probably best to think of a C# reference type variable being like a C++ pointer, and a C# `ref` parameter being like a C++ pointer to a pointer, at least as far as C# "reference" types go (i.e. all classes). For C# value types (structs and ints for example), they are like C++ value types and a `ref` parameter to a value type in C# is like a pointer in C++. – Matthew Watson Apr 25 '13 at 06:31
  • 2
    Jon Skeet has written a good article about parameter passing, describing in detail the differences. You can read it [here](http://www.yoda.arachsys.com/csharp/parameters.html) – default Apr 25 '13 at 07:00

6 Answers6

42

In C#, when you see something referring to a reference type (that is, a type declared with class instead of struct), then you're essentially always dealing with the object through a pointer. In C++, everything is a value type by default, whereas in C# everything is a reference type by default.

When you say "ref" in the C# parameter list, what you're really saying is more like a "pointer to a pointer." You're saying that, in the method, that you want to replace not the contents of the object, but the reference to the object itself, in the code calling your method.

Unless that is your intent, then you should just pass the reference type directly; in C#, passing reference types around is cheap (akin to passing a reference in C++).

Learn/understand the difference between value types and reference types in C#. They're a major concept in that language and things are going to be really confusing if you try to think using the C++ object model in C# land.

The following are essentially semantically equivalent programs:

#include <iostream>

class AClass
{
    int anInteger;
public:
    AClass(int integer)
        : anInteger(integer)
    {  }

    int GetInteger() const
    {
        return anInteger;
    }

    void SetInteger(int toSet)
    {
        anInteger = toSet;
    }
};

struct StaticFunctions
{
    // C# doesn't have free functions, so I'll do similar in C++
    // Note that in real code you'd use a free function for this.

    static void FunctionTakingAReference(AClass *item)
    {
        item->SetInteger(4);
    }

    static void FunctionTakingAReferenceToAReference(AClass **item)
    {
        *item = new AClass(1729);
    }
};

int main()
{
    AClass* instanceOne = new AClass(6);
    StaticFunctions::FunctionTakingAReference(instanceOne);
    std::cout << instanceOne->GetInteger() << "\n";

    AClass* instanceTwo;
    StaticFunctions::FunctionTakingAReferenceToAReference(&instanceTwo);
    // Note that operator& behaves similar to the C# keyword "ref" at the call site.
    std::cout << instanceTwo->GetInteger() << "\n";

    // (Of course in real C++ you're using std::shared_ptr and std::unique_ptr instead,
    //  right? :) )
    delete instanceOne;
    delete instanceTwo;
}

And for C#:

using System;

internal class AClass
{
    public AClass(int integer)
        : Integer(integer)
    {  }

    int Integer { get; set; }
}

internal static class StaticFunctions
{
    public static void FunctionTakingAReference(AClass item)
    {
        item.Integer = 4;
    }

    public static void FunctionTakingAReferenceToAReference(ref AClass item)
    {
        item = new AClass(1729);
    }
}

public static class Program
{
    public static void main()
    {
        AClass instanceOne = new AClass(6);
        StaticFunctions.FunctionTakingAReference(instanceOne);
        Console.WriteLine(instanceOne.Integer);

        AClass instanceTwo  = new AClass(1234); // C# forces me to assign this before
                                                // it can be passed. Use "out" instead of
                                                // "ref" and that requirement goes away.
        StaticFunctions.FunctionTakingAReferenceToAReference(ref instanceTwo);
        Console.WriteLine(instanceTwo.Integer);
    }
}
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • thanks for a great answer :) so you want to say, that it's similar to a double-pointer referring? – Secret Apr 25 '13 at 06:28
  • 4
    @Oleg: Yes. And no. If the item in C# is in fact a value type (that is, was declared as `struct`), then `ref` is only one indirection. Otherwise it is two. – Billy ONeal Apr 25 '13 at 06:34
  • 2
    It's worth making the distinction that `ref` is (9 times out of 10) not useful for class parameters - but when using value types, `ref int x` does make a difference. – Dan Puzey Apr 25 '13 at 07:14
  • `ref int` is more a different of semantics (as in, you're given the ability to change the caller's idea of what the value is) rather than efficiency, as I'd be astonished if passing a reference is faster than passing a 32-bit integer by value. The reference is probably a 32- or 64-bit value anyway. – Matthew Walton Apr 25 '13 at 09:27
  • @MatthewWalton You misunderstand. The integer is not passed as a pointer - you're using the caller's integer *directly* (if possible). The semantics are as if you passed a reference, but that's not what actually happens. So indeed, even `ref int` can make a difference - although it's nothing you can *rely* on - it's not part of the contract. Though the new compiler' support tail calls makes this less useful. And of course, value types are not really limited in size - you could be passing a 100-byte `struct`, or 1000-byte... even if it were passed as a pointer (it isn't), it makes a difference. – Luaan Jul 10 '15 at 11:38
  • 1
    I was specifically talking about `ref int`. Efficiency concerns do change with bigger value types of course. And I'm not sure how "using the caller's integer directly" is different to "the compiler passes a pointer to the caller's integer", because the callee has to be able to _find_ it. – Matthew Walton Jul 10 '15 at 12:23
  • This is it boys: "When you say "ref" in the C# parameter list, what you're really saying is more like a "pointer to a pointer." – Gaspa79 Aug 13 '19 at 13:46
  • @Gaspa79 If and only if the target is a reference type – Billy ONeal Aug 14 '19 at 01:21
  • @BillyONeal oh that's right, that generalization doesn't work for value types. – Gaspa79 Aug 14 '19 at 13:18
  • 1
    1. "everything is a reference type by default": There is no default in c#, so I don't know what this is supposed to mean. 2. "you're really saying is more like a "pointer to a pointer.": That is incorrect. When you pass a parameter using the "ref" modifier, you are passing the parameter by reference. It is NOT the same as a pointer. 3. All parameters are passed by VALUE in c#. When you pass an instance of a reference type, you are passing its pointer by value. When you pass an instance of a reference type using the "ref" modifier, you are passing its pointer by reference. – Orestis P. Jul 18 '22 at 20:52
25

A ref in C# is equivalent to a C++ reference:

  • Their intent is pass-by-reference
  • There are no null references
  • There are no uninitialized references
  • You cannot rebind references
  • When you spell the reference, you are actually denoting the referred variable

Some C++ code:

void foo(int& x)
{
    x = 42;
}
// ...
int answer = 0;
foo(answer);

Equivalent C# code:

void foo(ref int x)
{
    x = 42;
}
// ...
int answer = 0;
foo(ref answer);
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
3

Every reference in C# is pointer to objects on heap as pointer in C++ and ref of C# is same as & in C++

The reason ref should be avoided is, C# works on fundamental that method should not change the object passed in parameter, because for someone who does not have source of method may not know if it will result in loss of data or not.

String a = "  A  ";
String b = a.Trim();

In this case I am confident that a remains intact. In mathematics change should be seen as an assignment that visually tells is that b is changed here by programmer's consent.

a = a.Trim();

This code will modify a itself and the coder is aware of it.

To preserve this method of change by assignment ref should be avoided unless it is exceptional case.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167
  • 1
    So, to prevent data loses you may use some locker-models like a semaphore or use smth similar to a mutable objects in C++ with a correct pattern, if it's the only reason of a fear. – Secret Apr 25 '13 at 06:32
  • 5
    *"Everything in C# is pointer to objects on heap as pointer in C++"* That's not true. It's true for *reference* types, but not for *value* types. – Matthew Watson Apr 25 '13 at 06:32
  • 2
    Also, I would disagree that one is never supposed to change the state of passed parameters. Sure, immutable design is great, but most C# APIs are not immutable. Consider, for instance, any API taking a stream. Writing data into the stream certainly requires mutating it. – Billy ONeal Apr 25 '13 at 06:38
  • @BillyONeal for third party integration and Compatibility come under exceptional cases otherwise show me a pure C# API that has ref parameter. – Akash Kava Apr 25 '13 at 06:41
  • @OlegOrlov It is not data loss by memory corruption but it is a way of programming, it is important when someone else who will use your code should be confident that his variables will remain intact and you will not change it. – Akash Kava Apr 25 '13 at 06:50
  • @Akash: `System.Int32.TryParse` (or any of the `TryParse` methods). Also the `TryGetValue` methods on dictionaries. But you can mutate the input parameters even if the API is not a "ref" API. – Billy ONeal Apr 25 '13 at 18:46
  • @BillyONeal Check again, they are 'out' not 'ref' and that was specially designed keyword to let users know that it is going to modify it. And they are exceptional cases in immutable design to Try for success pattern. – Akash Kava Apr 25 '13 at 18:53
  • 1
    @Akash: Out and ref are the same thing as far as the CLR is concerned. In any case, my point above has nothing to do with out or ref parameters. Plain old normal passing of reference types does not mean that functions accepting such reference types won't call any mutating methods on the reference type passed. – Billy ONeal Apr 25 '13 at 19:00
  • The fact that a parameter is passed as a `ref` parameter should make it abundantly clear that the passed variable will be modified; something like `Interlocked.CompareExchange` would be pretty useless if it couldn't take a `ref` parameter. – supercat Feb 18 '15 at 00:17
1

C# has no equvalent of C++ pointers and works on references. ref adds a level of indirection. It makes value type argument a reference and when used with reference type it makes it a reference to a reference.

In short it allows to carry any changes to a value type outside a method call. For reference type it allows to replace the original reference to a totally different object (and not just change object content). It can be used if you want to re-initialize an object inside a method and the only way to do it is to recreate it. Although I would try avoid such an approach.

So to answer your question ref would be like C++ reference to a reference.

EDIT

The above is true for safe code. Pointers do exist in unsafe C# and are used in some very specific cases.

Maciej
  • 7,871
  • 1
  • 31
  • 36
  • http://msdn.microsoft.com/en-us/library/y31yhkeb.aspx might be interesting for you. – AakashM Apr 25 '13 at 09:29
  • 1
    Thanks for the link. Of course you can do almost anything in unsafe code but that is not the point of C# in general. – Maciej Apr 25 '13 at 09:56
  • 1
    Pointers aren't necessarily 'for interop'. To say "C# has no equvalent of C++ pointers" is simply false. – AakashM Apr 25 '13 at 10:26
1

This seems like a disposing/eventing nightmare. If I have an object who's events are registered for and pass it into a function by reference and that reference is then reallocated, the dispose should be called or the memory will be allocated until the program is closed. If the dispose is called everything registered to the objects events will no longer be registered for and everything it is registered for will no longer be registered for. How would someone keep this straight? I guess you could compare memory addresses and try to bring things back to sanity if you don't go insane.

clarkewu
  • 11
  • 1
-1

in c# you can check run unsafe in your project properties and then you can run this code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Exercise_01
{
    public struct Coords
    {
        public int X;
        public int Y;
        public override string ToString() => $"({X}, {Y})";
    }
    class Program
    {


        static unsafe void Main(string[] args)
        {
            int n = 0;
            SumCallByRefPointer(1, 2, &n);
            Console.Clear();
            Console.WriteLine("call by refrence {0}",n);

            n = 0;
            SumCallByValue(3, 4, n);
            Console.WriteLine("call by Value {0}", n);

            n = 0;
            SumCallByRef(5, 6, ref n);
            Console.WriteLine("call by refrence {0}", n);

            Pointer();
            Console.ReadLine();
        } 


        private static unsafe void SumCallByRefPointer(int a, int b, int* c)
        {
            *c = a + b;
        }

        private static unsafe void SumCallByValue(int a, int b, int c)
        {
            c = a + b;
        } 

        private static unsafe void SumCallByRef(int a, int b, ref int c)
        {
            c = a + b;
        }


        public static void Pointer()
        {
            unsafe
            {
                Coords coords;
                Coords* p = &coords;
                p->X = 3;
                p->Y = 4;
                Console.WriteLine(p->ToString());  // output: (3, 4)
            }
        }
    }
}