J. Doe
J. Doe

Reputation: 363

How to return a value of unknown type?

I have a very abstract problem ahead of me.

I have a class that has a public property of type Dictionary<string, object> the job of this dictionary is to hold custom user settings and their values.

There are no constraints on the settings but generally they are similar to:
"Brightness", 100
"Connection, "TCP"

So when I instantiate this class its dictionary property will be filled with the names of the settings and their corresponding values.

The problem is retrieving a value.
Since the values can be ints, strings, etc. they are stored as an object in the dictionary but I need to retrieve them as a concrete type.

While I can use dict["Brightness"].GetType() to find out what the type or use pattern matching I am having trouble figuring out what method return type to use.

I thought that, maybe, I can use a separate "result" property for each type and based on the value's type assign the corresponding value to the correct property, so like if the "Brightness" is 100, then have a method that tests the type of the value of "Brightness" and upon seeing it is int do something like

int resultInt;
resultInt = (int)dict["Brightness"];

But something tells me this is ugly and there is a better way.
Furthermore this is not threadsafe.

Additional information on the program's purpose:
The program's idea is to be able to, basically, store a bunch of settings and their values for different systems. They are always in a key-value relationship, keys are unique and values can be anything. They can be strings, ints, floats, decimal, etc. The purpose of the program is user customisation, in a way.
What I mean by this is - the user enters what settings he wants stored. What he enters becomes the full list of possible settings that the program will work with. He then enters values for these settings for each system or "device" that he has. So, for example, he says "I have a computer and a smartphone". Enters the settings "Brightness" and "CPU" into the program. Now the program knows all systems he configures will have these two settings with possibly different values associated with them.
So then the user can say "I want my smartphone screen brightness to be 80%." so he enters the int "80" as the value of "Brightness" for the entry that will correspond to his smartphone. Similarly he can enter a different value for his home computer.

Bottom line I never know what types will need to be stored.

Upvotes: 4

Views: 2662

Answers (4)

Patrick Hofman
Patrick Hofman

Reputation: 156918

Updated: As per the comments and your clarification: there is no way to write your code type-safe when you don't know the type on beforehand. There is no use in it.

So you have to keep using reflection or your own way of registering the type metadata (an enum or class containing some useful information on its type).

You can use your own metadata as decribed above to build your UI, do data validation and required casting in specific cases.


Old and less relevant: If you know the type on compile time, you could use generics to make it look a little more streamlined:

public T GetValueOrDefault<T>(string key)
{
    if (this.dictionary.TryGetValue(key, out object o))
    {
        return (T)(object)o;
    }

    return default(T);
}

For sure you have to check if your design makes sense, but I can't judge that right now from what you have written.

Upvotes: 4

MakePeaceGreatAgain
MakePeaceGreatAgain

Reputation: 37000

But something tells me this is ugly and there is a better way.

I completely agree your assumption.

It seems like you actually have some knowledge about the actual types but just want a single place to de-serialize those information. Assuming this having a single datastructure that represents completely unreleated things is a bad idea, as you end up with some weird casting- and typechecking-solution-nightmare. In particular you could easily write code that will fail at runtime:

var a = (int)dict["Connection"];

Instead you should have a statically typed datatype that represents those information. Create a class that has all those attributes and use some serializer-library, such as Newtonsofts JSON.Net to de-serialize the attributes into that datastructure.

class Settings
{
    public int Brightness { get; set; }
    public string Connection { get; set; }
}

Now you can easily de-serialize your settings:

var setting = JsonConvert.DeserializeObject<Settings>(myJson)

and have completely deterministic behaviour:

var b = setting.Brightness; // no casting to int neccessary

Upvotes: 0

Nathanael Demacon
Nathanael Demacon

Reputation: 1217

If you don't know at compile time what type is in your dictionary, you can use the dynamic tag (reworking the example of Patrick):

public dynamic GetValueOrDefault(string key)
{
    if (this.dictionary.TryGetValue(key, out object o))
    {
        return o;
    }

    return null;
}

Use case:

int val;

val = dict.GetValueOrDefault("Brightness");

Upvotes: -1

Kieran Devlin
Kieran Devlin

Reputation: 1433

You can use a format deserializer (e.g JSON / XML) to read an input and deserialize it to a given structure.

In your case you would define a class with the specific fields that you expect from this input file. (This example is using JSON but the principles are the same regardless of the notation format)

public class Settings {

    public int Brightness { get; set; }

    public string Connection { get; set; }

}

And then you would deserialize like so

var settings = JsonConvert.DeserializeObject<Settings>(fileInput)

The deserializer knows the type because you have defined it. (Note each field does not need to exist for the type to be created.)

Upvotes: 1

Related Questions