tags:

views:

129

answers:

4

Hi folks,

I'm having trouble trying to set the value of a property after i cast it. I'm not sure if this is called boxing.

Anyways, the new variable is getting set, but the original is not. I thought the new variable is just a reference to the original. But when i check the intellisence/debug watcher, the original property is still null.

here's the code.

// NOTE: data is either a Foo || FooFoo || FooBar, at this point.
//       only Foo impliments ITagElement.
if (data is ITagElement)
{
    var tags = ((ITagElement)data).TagList;
    // At this point, tags == null and data.TagList == null.
    if (tags.IsNullOrEmpty())
    {
        tags = new List<Tag>();
    }

    tags.Add(new Tag
    {
        K = xmlReader.GetAttribute("k"),
         V = xmlReader.GetAttribute("v")
    });

    // At this point, Tags.Count == 1 and data.TagList == null :( :( :(
}

Notice my inline comments about the values for tags and data.TagList ? Can some explain what I have done wrong? I thought the variable tags is just a reference pointer to the property data.TagList .. but it looks like it's not.

Update / Answer :)

thanks for the answers guys! it's embarassing cause i've been doing ths stuff for years and still forget/not notice simple things like this. (And of course, makes so much sence now that i see the light).

Marc got the points because his answer (IMO) was the simplest for my single blond brain cell.

Thanks all!

A: 

You have read from the TagList property. That makes tags hold the same value as TagList. But tags is not an alias for TagList. Changing either of them will not change the other. You assign a new value to the local tags variable, but that doesn't change the property you used to initialize that variable before. You need to assign the new tags value back into TagList.

Rob Kennedy
+10  A: 

Tags and data are completely isolated variables. Just because you assign an object to tags, this makes no difference to the variable data.

What you are getting confused is that when the two variables point to the same object, then changes to the (single) object will be seen through either variable.

Basically, if data starts non-null, you have

  • data: points to "list A"
  • tags: points to "list A"

In this scenario, adding an object to either data or tags is actually adding an object to the same "list A"

However, if data starts null, you have:

  • data: points to null
  • tags: points to null

You then assign tags, giving:

  • data: points to null
  • tags: points to "list B"

You would have to manually assign the "list B" to data to make it stick.

There is, however, one way to get it (assigning an interface/object) to work: ref and generics:

using System.Collections.Generic;
using System;
class Foo : IBar
{
    List<string> list = new List<string>();
    public int Count { get { return list.Count; } }

    void IBar.Add(string s) { list.Add(s); }
}
interface IBar
{
    void Add(string s);
}

static class Program
{
    static void Main()
    {
        Foo foo = null; // note  foo starts as null
        CreateAndAdd(ref foo, "abc");
        Console.WriteLine(foo.Count); // prove non-null and added
    }
    static void CreateAndAdd<T>(ref T data, string s) where T : IBar, new()
    {
        if (data == null) { data = new T(); } // create
        data.Add(s); // mutate
    }
}
Marc Gravell
Thanks marc - best summarized answer. cheers mate!
Pure.Krome
Take a look at the (just added) generics example too - it might be useful
Marc Gravell
Interesting (the new addition), makes sence too. That said, i'm not a fan of having objects instantiated (eg. list = new List<T>) if they might not be used. I like to have nulls. I instantiate at the last possible moment, but that's just schemantics :) Point is, the use of ref + generics is funky!
Pure.Krome
A: 

You're re-setting your reference (i.e. tags) but this doesn't change the value of the TagList property. This is always true for such assignments. Only changes to the content (i.e. your Add calls) are carried across references for the same object.

To make it so, you need to assign a new List<> instance to the original property and your tags variable. Then you can manipulate either with the desired result.

By the way, I presume IsNullOrEmpty is an extension method?

Konrad Rudolph
+2  A: 

You need to understand the difference between a reference to an object, and a reference to a variable/property.

When you read the .TagList property, what you get is a copy of the reference it stores. Assuming the type of the TagList property is List<Tag>, that's what you get a copy of.

It's important that you now understands that if TagList already holds a reference to an existing list, now you have two references to that list. One in the TagList property, and one in your tags variable.

But, since both refer to the same, single, list in memory, if you add something to it, then that list will contain the new item, regardless of how you get to that list.

However, in your code here now, you discover that tags, which holds a copy of whatever TagList held, is null, so you construct a new list and put that into tags. This makes tags differ from TagList in that TagList still holds a null reference. tags isn't linked in any way to the TagList property, and changes to tags won't automatically change TagList.

So basically you need to store that reference back into the TagList property, otherwise it will still be null when your code finishes executing.

For an explanation of how references work, see my answer here: Understanding pointers. Since references are at heart pointers, the same explanation is still true.

What you have done, if you follow my explanation, is written down a copy of the address on one piece of paper, onto another piece of paper, ie. copied it. Once you discover that your copy-paper is empty, you write the address of a newly constructed house on your copy-paper, but the original paper is still empty.

Lasse V. Karlsen