16

A bit of a weird question but I was wondering anyone could help...

In C++, I could do something like this

class MyOtherClass
{
     private:
         MyLogger* logger;
     public:
         MyOtherClass (MyLogger* logger)
              : logger (logger)
         {}
};

class MyClass
{
     private:
         MyLogger* logger;
     public:
         MyClass (MyLogger* logger)
              : logger (logger)
         {}
};

int main (int c, char** args)
{
    MyLogger* logger = new MyLogger ();
    /* Code to set up logger */
    MyOtherClass* myOtherClass = new MyOtherClass (logger);
    MyClass* myClass = new MyClass (logger);
}

So that each of the other objects (myOtherClass and myClass) would contain a pointer to logger, so they would be calling the same logger class. However, how would I achieve the same thing in C#? Is there a way to store a reference or pointer to a global object - I'm guessing that in C# if I do something like

public class MyClass
{
     private MyLogger logger = null;

     public MyClass (MyLogger _logger)
     {
         logger = _logger;
     }
};

that its actually assigning the class variable logger to a copy of _logger? Or am I'm mixing things up :S

Any help is very much appreciated, and thank you in advance!

Marc
  • 16,170
  • 20
  • 76
  • 119
KingTravisG
  • 1,316
  • 4
  • 21
  • 43
  • You should read up on Singletons: http://en.wikipedia.org/wiki/Singleton_pattern – RvdK Nov 08 '12 at 16:23
  • 2
    I have no idea why singletons are related to this. AFAIK there's no reason you can't have more than one logger. – R. Martinho Fernandes Nov 08 '12 at 16:29
  • Singletons are often an anti-pattern. Cf. https://stackoverflow.com/questions/12755539/why-is-singleton-considered-an-anti-pattern – Rob K May 09 '19 at 17:19

4 Answers4

22

It's actually a lot simpler in C#.

Basically, you can do this:

MyLogger logger = new MyLogger();
MyOtherClass myOtherClass = new MyOtherClass(logger);
MyClass myClass = new MyClass(logger);

In C#, the classes are basically kept around as references (really just pointers under the hood). In this snippet, you are passing the reference to logger to the constructors of both objects. That reference is the same, so each instance has the same MyLogger instance.

In this particular instance, you pretty much just need to remove the pointer syntax =D

Tejs
  • 40,736
  • 10
  • 68
  • 86
  • Yeah, I was thinking it was that way but then I thought it couldn't be that easy! Suppose this is what happens when your more use to C++ than C# :P – KingTravisG Nov 08 '12 at 16:27
  • 1
    Yeah. One of the fundamental gotchas compared to C++ is that object variables are really pointers under the hood... mostly. Always exceptions of course =D – Tejs Nov 08 '12 at 16:28
  • 1
    Ah right, so basically in C# the MyLogger logger = new MyLogger () is the eqivilant of C++ MyLogger* logger = new MyLogger ()? – KingTravisG Nov 08 '12 at 16:30
  • You got it. `logger` is a reference to an object, not the full object itself. You also typically dont have to worry about `delete`ing that object either, just set the reference to null or assign it a new reference later. The garbage collector will take care of pruning that memory later. – Tejs Nov 08 '12 at 16:31
  • Just to clear up one more thing confusing me at the moment - should I be passing (in C#) logger as a ref, or as it is? e.g. MyOtherClass (ref MyLogger logger) rather than MyOtherClass (MyLogger logger), or does it not make any difference (I'm still assuming the first is passing the address, the second is passing by value so copying?) – KingTravisG Nov 08 '12 at 16:36
  • 1
    You don't need any special modifier. Technically, all references _pass by value_, meaning the pointer value is is copied and passed in, but it's still a reference to the actual instance. `ref` is only required when you want to _pass by reference_ your object pointer, giving the method the ACTUAL reference to your instance. Typically that is used when you want the reference to your instance to change within the method. When passing by value, your method could internally assign a new reference, but outside the scope of that method, the actual reference has not changed. – Tejs Nov 08 '12 at 16:38
  • @SCassidy1986, read this article about Value vs Reference types in C#. It's really helpful and will probably clear up a lot of your confusion: http://www.albahari.com/valuevsreftypes.aspx – Dave Zych Nov 08 '12 at 16:40
  • 1
    @SCassidy1986 Using `ref` there would be the C++ equivilant of passing a pointer to a pointer to the object. If that's really what you need (i.e. you need to be able to change what the pointer points to in the fuction, rather than mutating the underlying object) then so be it. Such cases are rare though. – Servy Nov 08 '12 at 16:40
  • Ah right, think I've finally got my head around it now, many thanks, you've just saved me alot of headaches :) – KingTravisG Nov 08 '12 at 16:42
4

You're mixing things up. In C#, assignment statements such as

    logger = _logger;

copy references, not objects. After this statement executes, there is still (at most) only one MyLogger - it's now referred to by two object variables.

AakashM
  • 62,551
  • 17
  • 151
  • 186
2

If the type is a reference type (which is the case for classes), then you will copy the reference, not the object itself.

In opposition to reference type, you have value types. Values types are basically basic types : int, double, etc,

In your case, that means that you will work with the same objects, whether you access it from the class, or from the outer calling method. It's because you are targeting the referenced object.

Steve B
  • 36,818
  • 21
  • 101
  • 174
  • `string` is a reference type. – R. Martinho Fernandes Nov 08 '12 at 16:30
  • It actually behaves as a value type. If you pass a string to a method, and if you update the string in this method, to calling method will have the value of parameter unchanged. Somewhere here, [Eric Lippert explained this decision](http://stackoverflow.com/a/3660144/588868). – Steve B Nov 08 '12 at 16:31
  • I have no idea what you are talking about: you cannot mutate strings *at all*. – R. Martinho Fernandes Nov 08 '12 at 16:32
  • And this is a good thing. What I tried to said, is that if you call `string s = "hello";SomeMethod(s);" `s` will always be "Hello", whatever some method did with the parameter. – Steve B Nov 08 '12 at 16:33
  • Yes, and it means that "and if you update the string in this method" makes no sense. – R. Martinho Fernandes Nov 08 '12 at 16:34
  • I still don't think there's a need to single out strings: it only serves to sow confusion. It's a reference type, and it behaves as a reference type: `string s = null;`. Value types cannot be null. – R. Martinho Fernandes Nov 08 '12 at 16:38
  • I've updated my answer to be more accurate. thanks. If you think this requires additional clarification, do not hesitate to update the answer (I've marked the answer as community wiki) – Steve B Nov 08 '12 at 16:43
  • It's still not accurate! Because strings are reference types, *no values are copied* when you pass them around! They behave just like any other reference type. There is no magic. The only thing particular about strings is that there is no method that modifies the values. That is orthogonal to being a reference or value type. See here http://stackoverflow.com/a/3655991/46642 – R. Martinho Fernandes Nov 08 '12 at 16:45
  • Ok I get it. I simply removed the string in my answer. – Steve B Nov 08 '12 at 16:47
0

adding comment for future me as sometime I also forget, also for someone new in c# want to visualize "c++ pointer" vs "c# object reference" vs "C# object reference By Ref". Notice how Passing By Ref(Last method call) and assigning new obj, changes original object.

using System;

public class Emp
{
    public int TimeSpentInCompany {get; set;}
}

public class Program
{
    public static void Main()
    {
        
        Emp t1 = new Emp{TimeSpentInCompany = 5};
        Console.WriteLine("original obj before method call-->" + t1.TimeSpentInCompany);
        
        // Test is one by one
        
        // var res = PassObject_SimpleUpdateMemberAndPrint(t1);
        // var res = PassObject_SimpleUpdateObjectAndPrint(t1);
        // var res = PassObjectByRef_SimpleUpdateMemberAndPrint(ref t1);
        var res = PassObjectByRef_SimpleUpdateObjectAndPrint(ref t1);
        
        Console.WriteLine("original obj after method call-->" + t1.TimeSpentInCompany);
        Console.WriteLine("obj from method response-->" + res.TimeSpentInCompany);
    }
    
    static Emp PassObject_SimpleUpdateMemberAndPrint(Emp data)
    {
        /*
            original obj before method call-->5
            in method before modification obj member--> 5
            in method AFTER modification obj member--> 9
            original obj after method call-->9
            obj from method response-->9
        */      
        Console.WriteLine("in method before modification obj member--> "+ data.TimeSpentInCompany);
        data.TimeSpentInCompany += 4;
        Console.WriteLine("in method AFTER modification obj member--> "+ data.TimeSpentInCompany);
        return data;
    }   
    
    static Emp PassObject_SimpleUpdateObjectAndPrint(Emp data)
    {
        /*
            original obj before method call-->5
            in method before assigning new obj --> 5
            in method AFTER assigning new obj --> 9
            original obj after method call-->5
            obj from method response-->9
        */
        Console.WriteLine("in method before assigning new obj --> "+ data.TimeSpentInCompany);
        data = new Emp{TimeSpentInCompany = 9};
        Console.WriteLine("in method AFTER assigning new obj --> "+ data.TimeSpentInCompany);
        return data;
    }
    
    static Emp PassObjectByRef_SimpleUpdateMemberAndPrint(ref Emp data)
    {
        /*
            original obj before method call-->5
            in method before modification obj member--> 5
            in method AFTER modification obj member--> 9
            original obj after method call-->9
            obj from method response-->9
        */      
        Console.WriteLine("in method before modification obj member--> "+ data.TimeSpentInCompany);
        data.TimeSpentInCompany += 4;
        Console.WriteLine("in method AFTER modification obj member--> "+ data.TimeSpentInCompany);
        return data;
    }   
    
    static Emp PassObjectByRef_SimpleUpdateObjectAndPrint(ref Emp data)
    {
        /*
            original obj before method call-->5
            in method before assigning new obj --> 5
            in method AFTER assigning new obj --> 9
            original obj after method call-->9
            obj from method response-->9
        */
        Console.WriteLine("in method before assigning new obj --> "+ data.TimeSpentInCompany);
        data = new Emp{TimeSpentInCompany = 9};
        Console.WriteLine("in method AFTER assigning new obj --> "+ data.TimeSpentInCompany);
        return data;
    }
}
PKV
  • 773
  • 1
  • 7
  • 17