25

How do you define a getter and setter for complex data types such as a dictionary?

public Dictionary<string, string> Users
{
    get
    {
        return m_Users;
    }

    set
    {
        m_Users = value;
    }
}

This returns the entire dictionary? Can you write the setter to look and see if a specific key-value pair exists and then if it doesn't, add it. Else update the current key value pair? For the get, can you return a specific key-value pair instead of the whole dictionary?

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Mausimo
  • 8,018
  • 12
  • 52
  • 70

6 Answers6

35

Use an indexer property (MSDN):

public class YourClass
{
    private readonly IDictionary<string, string> _yourDictionary = new Dictionary<string, string>();

    public string this[string key]
    {
        // returns value if exists
        get { return _yourDictionary[key]; }

        // updates if exists, adds if doesn't exist
        set { _yourDictionary[key] = value; }
    }
}

Then use like:

var test = new YourClass();
test["Item1"] = "Value1";
James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • Good one! But as said by Nicola Anusev, while setting the property, you should check if it already exists with `_yourDictionary.ContaineKey(key)`. And if ot doesn't exist, you should set it with `yourDictionary.Add(key, value)` – Yoone Jun 12 '13 at 08:03
  • 2
    This will only work if the class is not static, might want to think of an alternative for an static class... – TGarrett May 18 '15 at 13:42
  • @TGarrett It's a wrapper class for a non-static Dictionary class. If anything it can be made generic, but I don't see the need for a static implementation. If you do, I'm all ears! – Benj Sanders Dec 07 '18 at 18:34
22

It is not possible to do it in a way that would involve only properties. You theoretically could write a setter, but for a getter, you would need to specify a key that you want to retrieve. That is impossible since properties do not accept parameters. Natural way to accomplish what you want would be to use methods:

private Dictionary<string, string> users = new Dictionary<string, string>();

public void Set(string key, string value)
{
    if (users.ContainsKey(key))
    {
        users[key] = value;
    }
    else
    {
        users.Add(key, value);
    }
}

public string Get(string key)
{
    string result = null;

    if (users.ContainsKey(key))
    {
        result = users[key];
    }

    return result;
}

Alternatively, as others have already said, you could use indexers, but I've always found them a little cumbersome. But I guess it's just a matter of personal preference.

And just for the sake of completeness, this is how a setter could look like, although it's highly unusual and counter-intuitive to have such a property:

public KeyValuePair<string, string> Users
{
    set
    {
        Set(value.Key, value.Value);
    }
}

Internally, it uses the Set method from my previous snippet.

Nikola Anusev
  • 6,940
  • 1
  • 30
  • 46
  • 2
    It's worth noting you could technically use a property to set values (just have the property of type 'keyvaluepair'). You just couldn't use a `get`ter to get the value (or pair) for a key since you can't specify a key. – Servy Jul 20 '12 at 16:48
  • @Servy Of course, *technically*, you could, but it would be quite unnatural. – Nikola Anusev Jul 20 '12 at 16:56
  • 1
    I wouldn't have said anything normally, but not only did you say it's technically not possible (which is true) you say it's technically not possible for *both cases* (which is not true). It's *impossible* for a getter, and *inconvenient and un-intuitive* through a setter. – Servy Jul 20 '12 at 17:04
  • @Servy You are right. Reminds me to be careful when wording my answers :) – Nikola Anusev Jul 20 '12 at 17:34
1

You won't be able to do that with a property. You'll need to use methods for that, or add an indexer to your class. The get method can't accept a parameter (the key).

Another option, if you want someone to be able to easily add/remove keys to the dictionary but prevent them from setting an entirely new one would be to make the property a read-only property that returns a dictionary created in the constructor. It would be less flexible then adding get/set methods, but in common, simple cases it can do just fine.

Servy
  • 202,030
  • 26
  • 332
  • 449
1

It looks like you want an "named indexer". Here's (my) one way to accomplish that using C#.

My approach exposes a property that returns an object (with a default indexer) which will perform the indexing into the appropriate field given the lambdas to do it.

There are reasons you may or not want to use this method, but I'll leave that to you. :)

Ani
  • 10,826
  • 3
  • 27
  • 46
  • 2
    Why not just use an indexer directly? – James Michael Hare Jul 20 '12 at 16:48
  • Because I may have multiple indexers on a class that take the same parameter types. Children[int index] and Customers[int index]. Also, the OP's use case shows the use of a name as in obj.Foo[...], not obj[...] – Ani Jul 20 '12 at 16:50
  • 1
    The link is not working anymore. Please update and add the essentials of the solution to your answer so that this won't happen again. – Jan May 24 '21 at 19:57
0

It is possible to do so with the setter but highly unrecommended, and is completely impossible with the getter as it takes no parameter to determine what to get.

For the setter you would have to pass a Dictionary<string, string> with a single pair but it goes against what you would expect the getter/setter to usually do and completely stops you setting the entire Dictionary.

A much better way is to use a pair of methods which you can name Get and Set if you so desire.

Graham Wager
  • 1,887
  • 1
  • 19
  • 28
0
Dictionary<string, string> param = new Dictionary<string, string>();

public void SetYourParameter(string parametrName, string paramValue)
{
    param[parametrName] = paramValue;            
}

public string GetYourParameter(string parametrName)
{
    // ContainKey ---> It returns value if the key was found
    if( param.ContainsKey(parametrName))
        return param[parametrName];
    else
        return null;
}
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
NG5121472
  • 31
  • 1
  • 3