C# ref is it like a pointer in C/C++ or a reference in C++?

48,086

Solution 1

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);
    }
}

Solution 2

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);

Solution 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.

Solution 4

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.

Solution 5

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.

Share:
48,086
Secret
Author by

Secret

Updated on August 05, 2020

Comments

  • Secret
    Secret almost 4 years

    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
    Secret about 11 years
    thanks for a great answer :) so you want to say, that it's similar to a double-pointer referring?
  • Secret
    Secret about 11 years
    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.
  • Matthew Watson
    Matthew Watson about 11 years
    "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.
  • Billy ONeal
    Billy ONeal about 11 years
    @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
    Billy ONeal about 11 years
    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.
  • Akash Kava
    Akash Kava about 11 years
    @BillyONeal for third party integration and Compatibility come under exceptional cases otherwise show me a pure C# API that has ref parameter.
  • Akash Kava
    Akash Kava about 11 years
    @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.
  • Dan Puzey
    Dan Puzey about 11 years
    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.
  • Matthew Walton
    Matthew Walton about 11 years
    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.
  • AakashM
    AakashM about 11 years
    msdn.microsoft.com/en-us/library/y31yhkeb.aspx might be interesting for you.
  • Maciej
    Maciej about 11 years
    Thanks for the link. Of course you can do almost anything in unsafe code but that is not the point of C# in general.
  • AakashM
    AakashM about 11 years
    Pointers aren't necessarily 'for interop'. To say "C# has no equvalent of C++ pointers" is simply false.
  • Billy ONeal
    Billy ONeal about 11 years
    +1 because this contains a code example for what value types do in C#.
  • Billy ONeal
    Billy ONeal about 11 years
    @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.
  • Akash Kava
    Akash Kava about 11 years
    @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.
  • Billy ONeal
    Billy ONeal about 11 years
    @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.
  • Maciej
    Maciej about 11 years
    This answer is incomplete - what about ref on reference types?
  • fredoverflow
    fredoverflow about 11 years
    There is nothing special about a ref on reference types. The C++ equivalent would be a reference to a pointer: T*&.
  • supercat
    supercat about 9 years
    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.
  • Luaan
    Luaan almost 9 years
    @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.
  • Matthew Walton
    Matthew Walton almost 9 years
    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.
  • Gaspa79
    Gaspa79 over 4 years
    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."
  • Billy ONeal
    Billy ONeal over 4 years
    @Gaspa79 If and only if the target is a reference type
  • Gaspa79
    Gaspa79 over 4 years
    @BillyONeal oh that's right, that generalization doesn't work for value types.