Reputation: 6842
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
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
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
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
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