35

When should you put Key/Value type of data in it's own class instead of using a pre-built generic structure, such as a KeyValuePair or a Tuple?

For example, most ComboBoxes I create contain a DisplayName and a Value. This is the kind of data I am trying to decide when to put in a new class, and when to just use a KeyValuePair.

I am currently working on something that uses iCalendar, and the selected user's data ultimately gets combined into a key1=value1;key2=value2; type of string. I started out by putting the data in a KeyValuePair<string,string>, but now I am wondering if that should be it's own class instead.

Overall, I am interested in finding out what guidelines are used when deciding to use an existing structure/class like a KeyValuePair over a 2-property object, and in what kind of situations you would use one over another.

12
  • 3
    What language? In Python, we don't have this dilemma, because we have tuple type.
    – S.Lott
    Sep 13, 2011 at 13:30
  • 3
    @S.Lott - The .NET BCL has tuples since v 4.0.
    – Oded
    Sep 13, 2011 at 13:35
  • 1
    .NET 4 also has the tuple type. msdn.microsoft.com/en-us/library/system.tuple.aspx
    – Dave Nay
    Sep 13, 2011 at 13:36
  • 1
    @Oded In this case, I am building something with iCalendar and I wanted objects for BYDAY and BYSETPOS. They appear in the ComboBoxes, but the actual data is combined into the recurring rule string, which is a key=value; type of string
    – Rachel
    Sep 13, 2011 at 13:39
  • 4
    @Rachel: Please update the question to be more specific. A bunch of comments aren't the best way to clarify things.
    – S.Lott
    Sep 13, 2011 at 13:39

8 Answers 8

29

I'd generally use an object rather than a KeyValuePair or Tuple in most cases. First, when you come in 6 months later to make changes, it is alot easier to figure out what your intent was earlier rather than wondering what Tuple t is and why it has those funny values. Second, as things grow and change you can easily give your simple data transfer objects behavior as required. Need to have two name formats? Easy, just add appropriate ToString() overloads. Need it to implement some interface? No problem. Finally, there really is nearly zero overhead to creating a simple object, especially with automatic properties and code completion.

Bonus Protip: if you want to keep these objects from polluting your namespace, making private classes inside of classes is a great way to keep things under wraps and prevent strange dependencies.

2
6

The rule for defining a new class is simple: It's summarized by "Occam's Razor".

http://c2.com/cgi/wiki?OccamsRazor

Do not introduce new classes without a good reason. Or use pre-built classes as much as possible. Or invent as little as possible. However you're comfortable writing less code.

You cannot use a pre-built class if you have to assign some unique responsibility to the object and that responsibility is not defined in the pre-built class. Often this is a unique method.

A tuple or KeyValuePair is preferred.

Until.

You need some functionality that's not part of A tuple or KeyValuePair. Then you have to define your own class.

6
  • 3
    "Never design what you can steal" Is about my favorite way of phrasing this. Sep 13, 2011 at 17:43
  • 3
    Would you consider semantics to be "functionality that's not part of a tuple or KeyValuePair"? KeyValuePair at least has some limited form of semantics, but tuples rarely do (might be possible, depending on context) imho. Introducing a new class should give you clear semantics as a matter of course.
    – Mal Ross
    Nov 29, 2011 at 13:47
  • +1 Don't patch a whole in a boat with duct tape if it can fit a plug. Jan 17, 2012 at 4:27
  • 8
    I think this is a misplaced use of Occam's razor. Secondly it is important to know what information is contained in a variable. -1.
    – Bent
    Jan 10, 2016 at 21:07
  • 2
    I've always considered naming things to be a "good reason" for distinguishing between what are otherwise two structurally identical classes!
    – Steve
    May 25, 2020 at 12:48
5

S.Lott wrote

Do not introduce new classes without a good reason. Or use pre-built classes as much as possible. Invent as little as possible.

You cannot use a pre-built class if you have to assign some unique responsibility to the object that is not defined in the pre-built class.

Let me say why I have a serious issue with this. Since

KeyValuePair [string,string]

seems to be okay .... is KeyValue [string, KeyValue [string, KeyValuePair [string, string]]] also okay ? I don't know what S.Lott will say to this, but my boss thinks it is okay.

I dare to disagree. And here's why : It reduces Readability of the code that will follow. The function that will fill up such a data structure will be much more complex and error (err .. exception) prone (just imagine at least some business logic as well). I didn't argue with my boss too much, but i will say it here : Readability trumps minute space savings (which was his argument). So wouldn't a class object in which there are some fields ; but some remain empty at some times be better ?

P.S. I am an Ultra_Noob so please tell me if I am wrong.

2
  • 2
    I think that KeyValuePair<string,string> is fine, assuming the things put in are indeed keys and values. Here, reusing an existing class is a win, since you save writing code and gain interoperability. OTOH, using something as complicated as KeyValuePair<string,KeyValuePair<string,string>> is most of the time simply too complicated to be practical. Sometimes it may be right -- when you need no additional functionality, when the meaning is obvious, and when it works nicely with other classes. YMMV, this is just weighting the pros and contras.
    – maaartinus
    Sep 13, 2011 at 19:57
  • 1
    If you are using C#, here is the performance of a Tuple vs KeyValuePair. Something you might want to share with your boss?
    – Ben
    Jun 7, 2015 at 15:33
4

If you write your own class you have a clear place to put the documentation about what the two values are. Especially since two strings isn't a very clear definition. However you could write something like

 public class MyPair : Tuple<string, string>
5
  • I like this because MyPair defines what the Tuple values are and what they are used for.
    – IAbstract
    Sep 13, 2011 at 15:47
  • 2
    But then your properties would be called Item1 and Item2! Isn't it just as easy to define the entire class? public class SideStrings { public string Left { get; set; } public string Right { get; set; } } Sep 13, 2011 at 18:03
  • @configurator I was writing a response to that and noticed that Tuple is read only so I agree which I think moots the whole thing. Although you could wrap the tuple to name the values whatever you want.
    – Sign
    Sep 13, 2011 at 19:05
  • 3
    @Sign: But what's the point? Creating your own class is less work than wrapping a tuple. Sep 13, 2011 at 19:37
  • 2
    @configurator you get some equality and comparison stuff that works better than a custom object, but if setting is required the tuple is definitely the wrong way to go.
    – Sign
    Sep 13, 2011 at 20:00
4

I would perhaps agree with Wilding here.

I would suggest that the built-in types are often mechanism objects. KeyValuePair for example, is part of the mechanism of a dictionary and hash tables, helping to ensure unique keys and they have properties that have meaningful names within the context of the mechanism itself.

On the other hand, using custom made data objects provides a better representation of your data and provide many benefits, including readability and extensibility. There is nothing to stop reusing existing code in custom built objects as Sign suggested, but I would try and hide the wrapped class, and avoid abstraction leakage, so that you can change the underlying implementation at a later point without affecting the rest of your code.

So the real question: Are you representing data or are you using the data in a mechanism? (and I wouldn't advise mixing these concepts together).

In my experience there is nothing worse that seeing a Tuple[string, string] being passed into a method and having no immediate way of knowing what Item1 and Item2 are supposed to be. Best to use Tuples within methods or as private members of a class only.

3

When we say key/value pair, I usually think of hash tables, or associative arrays, or simply a KeyValuePair object. I use all of them and there is no difference in when I use which.

I think the most important thing about a list of key/value pairs is that, keys should be unique (since it's a key after all), and all of the aforementioned structures really provide me with that functionality.

Other than that, I want to search by keys, or to do list (array) style actions like push and pop.

So, my answer is, NO, and I don't create objects explicitly for key/value pairs, since lots of built-in structures already do that for me.

3

Chapter six of Clean Code has a good description of when to use a data structure versus a class. In a nutshell, you use a data structure when you mostly anticipate adding new functions to operate on that data. You use a class when you mostly anticipate adding new data types to be operated on by existing functions. Your ComboBox is an example of the latter. You have one set of functions (the ComboBox implementation) operating on many different data types (different kinds of data for different widgets).

0

A display name and a value are quite clearly not a key/value pair so using a key/value pair would be totally inappropriate.

In Swift tuples (triples, quadruples etc) are a very easy way to create ad-hoc structs. Very useful to return and assign multiple values, like getting width and height of a rectangle. And their components can be named. But for something that is more long term, you’d create a struct or a class, whatever you feel is more appropriate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.