John Darvill
John Darvill

Reputation: 1303

Relating strings to Properties in C#

Say I have a class:

class Thing
{
    public string Id { get; set; }
    public string Foo { get; set; }
    public string Bar { get; set; }
}

And I have a sort of URL parameter style convention, where I want to allow the specification of values for this class in the following string format:

"{Id}~foo={foo}~bar={bar}"

I might want to write a class that does something like this:

class ThingHandler
{
    private static readonly IDictionary<string, (Func<Thing, string> GetValue, Action<Thing, string> SetValue)> dictionary =
        new Dictionary<string, (Func<Thing, string> GetValue, Action<Thing, string> SetValue)>
        {
            { "foo=", (new Func<Thing, string>((thing) => thing.Foo), new Action<Thing, string>((thing, value) => thing.Foo = value)) },
            { "bar=", (new Func<Thing, string>((thing) => thing.Bar), new Action<Thing, string>((thing, value) => thing.Bar = value)) },
        };

    public Thing Unstringify(string str)
    {
        Thing thing = new Thing();

        var split = str.Split('~');

        thing.Id = split.First();

        foreach (var keyValuePair in split.Skip(1))
        {
            var key = keyValuePair.Substring(0, 4);
            var value = keyValuePair.Substring(4);
            dictionary[key].SetValue(thing, value);
        }

        return thing;
    }

    public string Stringify(Thing thing)
    {
        StringBuilder stringBuilder = new StringBuilder(thing.Id);

        foreach (var item in dictionary)
        {
            var value = item.Value.GetValue(thing);
            if (!string.IsNullOrEmpty(value))
            {
                stringBuilder.Append(item.Key);
                stringBuilder.Append(value);
            }
        }

        return stringBuilder.ToString();
    }
}

It just seems very inelegant to have to specify a property in two different ways like this, and we end up with a lot of repeated code in those two dictionary entries. Reflection could be used to make this less messy possibly? But again, it feels like it shouldn't have to be to me. Something like this:

{ "foo=", Property<Thing>((thing) => thing.Foo) },
{ "bar=", Property<Thing>((thing) => thing.Bar) },

would be so much neater. Is there anything I can do with attributes (staying away from reflection) that might help in this instance?

Additionally, am I coming at this completely wrong? Is there a way of turning it on its head that makes handling the connection between a string and a property really succinct and neat? I seem to be encountering the need to relate strings to properties like this reasonably often at the moment, (or pass around a pointer to the property itself) and any advice would be much appreciated.

Upvotes: 0

Views: 100

Answers (3)

JamesHoux
JamesHoux

Reputation: 3447

This question is the OP's attempt to clarify his original question here: Is there a way to store a pointer to a property in C#?

The OP originally said that he is running into a need to do this quite regularly, which suggests he may be approaching the problem incorrectly. Looking at his new question above, it appears to be a serialization problem.

OP should go read everything he can about serialization and then see if he can put together an off the shelf solution. Other responders have suggested use of Newtonsoft JSON, which I also think may be an appropriate solution for the OP. Newtonsoft is known to be extremely fast, which makes it work on almost any situation. It's also pretty robust.

The only reason I would suggest NOT using Json serialization is if the OP needs his own more human-readable format. And even if that's his need, there are still some off-the-shelf ways of achieving that. Again, OP should read everything he can about serialization before proceeding.

Upvotes: 1

Joel Coehoorn
Joel Coehoorn

Reputation: 415735

For just the one Thing type, one pattern is for the stringify methods to be members of the original class:

class Thing
{
    public string Id { get; set; }
    public string Foo { get; set; }
    public string Bar { get; set; }

    public static Thing FromString(string input)
    {
        var result = new Thing();
        var split = input.Split('~');

        result.Id = split[0];

        foreach (var keyValuePair in split.Skip(1))
        {
            var key = keyValuePair.Substring(0, 3).ToLower();

            if (key == "foo")
                result.Foo = keyValuePair.Substring(4);
            if (key == "bar")
                result.Bar = keyValuePair.Substring(4); 
        }

        return result;
    }

    public overrides string ToString()
    {
        return $"{Id}~foo={Foo}~bar={Bar}";
    }
}

Choosing to override the ToString() method can be especially nice, but you might also instead use paired static Builder or Factory classes if you need to handle several different formats, where the method or type names can indicate context.

However, using a dictionary seems like an attempt for make this work for any random type (or several specific types) with unknown or various properties. In this situation, where you want more reproducible and generic implementations, you may want to read up more on serialization. The link is just for the built-in stuff including in .Net. There are a number of additional libraries you can use to handle this kind of thing, as well.

Upvotes: 0

Bruno Belmondo
Bruno Belmondo

Reputation: 2357

Regarding the purpose of the code, as other have stated I would rather use Standard JSON serialization using Newtonsoft JSON for instance.

Regarding the property part of your question, there is no possibility to simply define a pointer to a property to avoid code duplication.

One thing though: you can use nameof() on a property like

class X
{
    bool Y {get;set;}
    void string foo() => nameof(Y);
}

new X().foo(); // return "Y"

In another stackoverflow question I proposed a way to create property pointers but that was more a proof of concept rather than a really usable thing: PropertyPointer

Upvotes: 0

Related Questions