tags:

views:

184

answers:

3

Hi All, I was working on a demo method and found something strange(at least to me :-))

enter code here  class Program
{
    public void AnotherSwap<T>(T a, T b)  
    {
        T temp;
        temp = a;
        a = b;
        b = temp;

        Console.WriteLine(a);
        Console.WriteLine(b);

    }


    public void swap<T>(T a, T b) where T : MyInt  // Passing without ref
    {
        object temp;
        temp = a.MyProperty;
        a.MyProperty = b.MyProperty;
        b.MyProperty = (int)temp;

        Console.WriteLine(a.MyProperty);
        Console.WriteLine(b.MyProperty);



    }

    static void Main(string[] args)
    {
        Program p = new Program();
        MyInt a = new MyInt() { MyProperty = 10 };
        MyInt b = new MyInt() { MyProperty = 20 };
        p.swap<MyInt>(a, b);
        Console.WriteLine(a.MyProperty); // changed values get reflected
        Console.WriteLine(b.MyProperty); // changed values get reflected

        Console.WriteLine("Another Swap");
        object x = 10;
        object y = 20;
        p.AnotherSwap(x, y);

        Console.WriteLine(x);  // changed values are not getting  reflected
        Console.WriteLine(y);  // changed values are not getting  reflected

         Console.ReadKey();


    }
   public class MyInt
    {
        public int MyProperty { get; set; }

    }




}

Here when I am calling the swap() , though I haven't mentioned ref , the changed values are automatically getting reflected (as in p.swap(a, b); a and b are instance of Myint and thus will by default work as ref..as per my understanding.) But same should happen with Anotherswap() here also I am passing object x,y but now the values are not getting reflected in Main() .i.e. now its working as a value type. Can somebody explain where my understanding is going wrong. Let me know if u guys want more info.

+1  A: 

When MyInt type is cast as Object, it gets cast as a val type. Object cannot be treated like a reference since its representing both val and ref, it must be explicitly passed by ref.

int can be cast as an object, and its still treated as a val. string can be cast as object, and its treated as a val. Even though normally it would be a ref type.

If you did the AnotherSwap and passed T in as MyInt, you would get the same results as before. If you called the original swap algorithm with objects, you would get the same output as the latter.

DevelopingChris
thanks Chris, I got the point.
Wondering
+1  A: 

the second set of the objects [object x = 1] is converted to int which is a value type since it is assigned an int at initialization, so it is treated as an int which is a value type.

if you run the following snippet you'll see my point.

object test = 1;
Console.Writeline(test.GetType());

returns: "typeof (Int32)"

so the type of your object is no longer object but Int32 which is a value type.

object test = 1;
Console.Writeline(test.GetType().IsByRef);

returns: "false"
Keivan
IsByRef isn't a test of whether a type is a reference type or not - it's used to see whether a parameter type is passed *by* reference. They're very different things. typeof(object).IsByRef is still false...
Jon Skeet
thanks, didn't know that. but isn't that kind of unintuitive to have this property exposed under Type class, shouldn't that be part of the Reflection namespace?
Keivan
+3  A: 

Your question is misleading, and the answer is actually simpler than that:

In your Swap function, you are swapping the values of the MyProperty properties in those classes, not the actual classes themselves. This would be easily proved if you had another property in the MyInt class called "Name" or something. After the call to Swap, you would see that the Name property remained unchanged.

The reason that in AnotherSwap nothing interesting is happening is because you aren't passing the types by ref at all, so the assignments that you perform to the object's pointer inside the function don't modify the original objects' pointers from the caller's perspective.

Something else to consider is that when you're turning 10 into an object, you're actually boxing the object, and that's something else you need to keep in mind. When writing generics that you want to behave differently for reference versus value types, I always just use "where T : class" for reference types and "where T : struct" for value types. The names of the two functions cannot be the same though.

scwagner