uncletall
uncletall

Reputation: 6842

How to pass an object instead of it value

I am trying to make a simple parser where I have key, value pairs that I would like to assign to variables. I want to map a String to a variable. I think the below test class should express what I would like to do. But how can I store a variable so I can update it?

[TestClass]
public class StringObjectMapperTest
{
    [TestMethod]
    public void TestMethod1()
    {
        string string1 = "";
        string string2 = "";
        StringObjectMapper mapper = new StringObjectMapper();
        mapper.Add("STRING1", ref string1);
        mapper.Add("STRING2", ref string2);
        mapper.Set("STRING1", "text1");
        Assert.AreEqual("text1", string1); // FAILS as string1 is still ""
    }
}

public class StringObjectMapper
{
    private Dictionary<string, string> mapping = new Dictionary<string, string>();

    public StringObjectMapper()
    {
    }

    public void Set(string key, string value)
    {
        string obj = mapping[key];
        obj = value;
    }

    internal void Add(string p, ref string string1)
    {
        mapping.Add(p, string1);
    }
}

Update: I am trying to use a Boxed String but this also seems to act like a immutable object, any idea why?

[TestClass]
public class StringObjectMapperTest
{
    [TestMethod]
    public void TestMethod1()
    {
        BoxedString string1 = "string1";
        BoxedString string2 = "string2";
        StringObjectMapper mapper = new StringObjectMapper();
        mapper.Add("STRING1", ref string1);
        mapper.Add("STRING2", ref string2);
        mapper.Set("STRING1", "text1");
        string s = string1;
        Assert.AreEqual("text1", s); // Fails as s = "string1" ???
    }
}

public struct BoxedString
{
    private string _value;

    public BoxedString(string value)
    {
        _value = value;
    }

    public void Set(string value)
    {
        _value = value;
    }

    static public implicit operator BoxedString(string value)
    {
        return new BoxedString(value);
    }

    static public implicit operator string(BoxedString boxedString)
    {
        return boxedString._value;
    }
}

public class StringObjectMapper
{
    private Dictionary<string, BoxedString> mapping = new Dictionary<string, BoxedString>();

    public StringObjectMapper()
    {
    }

    public void Set(string key, string value)
    {
        BoxedString obj = mapping[key];
        obj.Set(value);
    }

    internal void Add(string p, ref BoxedString obj)
    {
        mapping.Add(p, obj);
    }
}

Upvotes: 2

Views: 118

Answers (4)

uncletall
uncletall

Reputation: 6842

I guess the best way to solve this is by using delegates. Although the syntax is still a bit strange to me it seems the cleanest way to solve this. The below codes works:

[TestClass]
public class StringObjectMapperTest
{
    private Dictionary<string, Setter> mapping = new Dictionary<string, Setter>();

    public delegate void Setter(string v);

    [TestMethod]
    public void TestMethod1()
    {
        string string1 = "string1";
        string string2 = "string2";
        string text1 = "text1";
        string text2 = "text2";

        Add("STRING1", x => string1 = x);
        Add("STRING2", x => string2 = x);

        Assert.AreNotEqual(text1, string1);
        Set("STRING1", text1);
        Assert.AreEqual(text1, string1);

        Assert.AreNotEqual(text2, string2);
        Set("STRING2", text2);
        Assert.AreEqual(text2, string2);
    }

    private void Set(string key, string value)
    {
        Setter set = mapping[key];
        set(value);
    }

    private void Add(string p, Setter del)
    {
        mapping.Add(p, del);
    }
}

Upvotes: 1

Fredou
Fredou

Reputation: 20100

this is not what you want but this work... if you accept it :-)

use StringBuilder

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            StringBuilder string1 = new StringBuilder();
            StringBuilder string2 = new StringBuilder();
            StringObjectMapper mapper = new StringObjectMapper();
            mapper.Add("STRING1", ref string1);
            mapper.Add("STRING2", ref string2);
            mapper.Set("STRING1", "text1");
            Console.Write("text1" == string1.ToString());
            Console.ReadKey();
        }
    }

    public class StringObjectMapper
    {
        private Dictionary<string, StringBuilder> mapping = new Dictionary<string, StringBuilder>();

        public StringObjectMapper()
        {
        }

        public void Set(string key, string value)
        {
            StringBuilder obj = mapping[key];
            obj.Clear();
            obj.Append(value);
        }

        internal void Add(string p, ref StringBuilder string1)
        {
            mapping.Add(p, string1);
        }
    }
}

Upvotes: 3

D Stanley
D Stanley

Reputation: 152556

You can't do this as strings are immutable. Changing a string returns a new string. Even though you pass the string by reference in Add, the string value stored in the dictionary is still immutable. Here's what happens in Set:

public void Set(string key, string value)
{
    string obj = mapping[key];  // obj points to the string value at mapping[key]
    obj = value;                // obj points to the string value referenced by value - mapping[key] is unchanged.
}

To do what you want you'll need to "box" the string by using a true reference type - whether object or a class that wraps the string as the Dictionary's value type.

Upvotes: 4

NathanAldenSr
NathanAldenSr

Reputation: 7961

My first thought is that this isn't possible in C# without obtaining the pointer that ref string string1 represents. Unfortunately, .NET is garbage collected, which means the pointer can change unless you use a fixed block.

I think a better approach here is to use an interface that represents a way to set a string.

public interface IData
{
    void Set(string data);
}

public class StringObjectMapper
{
    private readonly Dictionary<string, IData> mapping = new Dictionary<string, IData>();

    public void Set(string key, string value)
    {
        if (key == null)
        {
            throw new ArgumentNullException("key");
        }
        if (value == null)
        {
            throw new ArgumentNullException("value");
        }

        mapping[key].Set(value);
    }

    internal void Add(string key, IData data)
    {
        if (key == null)
        {
            throw new ArgumentNullException("key");
        }
        if (data == null)
        {
            throw new ArgumentNullException("data");
        }

        mapping.Add(key, data);
    }
}

Upvotes: 2

Related Questions