Are (c#) dictionaries an Anti Pattern?Finding a definition for this anti-patternWhat are the relative advantages of dictionaries versus databases?TDD Mock call verification - is it an anti-pattern?Reference Passing Style Anti PatternPassing an object into a method which changes the object, is it a common (anti-) pattern?Efficiency of C# dictionariesA better way than O(n^2) for traversing a dictionary of dictionaries.Dictionary of dictionaries design in C#Does this anti-pattern have a name?
Pen test results for web application include a file from a forbidden directory that is not even used or referenced
Recommended Breathing Exercises to Play Woodwinds
How do I insert two edge loops equally spaced from the edges?
Defending Castle from Zombies
Did anybody find out it was Anakin who blew up the command center?
How to force GCC to assume that a floating-point expression is non-negative?
What's the point of fighting monsters in Zelda BoTW?
How to prevent a hosting company from accessing a VM's encryption keys?
Are strlen optimizations really needed in glibc?
Does trying to charm an uncharmable creature cost a spell slot?
Availability Groups automatic failover is not so automatic
Dual of a bimodule
Notice period 60 days but I need to join in 45 days
Alternatives to Network Backup
How to say "I only speak one which is English." in French?
Why did Lucius make a deal out of Buckbeak hurting Draco but not about Draco being turned into a ferret?
Can I get a PhD for developing an educational software?
What is the name of this plot that has rows with two connected dots?
Are there any to-scale diagrams of the TRAPPIST-1 system?
Units in general relativity
Is a memoized pure function itself considered pure?
Fantasy Macro Economics: What would Merfolk Trade?
Is a Centaur PC considered an animal when calculating carrying capacity for vehicles?
Is the Amazon rainforest the "world's lungs"?
Are (c#) dictionaries an Anti Pattern?
Finding a definition for this anti-patternWhat are the relative advantages of dictionaries versus databases?TDD Mock call verification - is it an anti-pattern?Reference Passing Style Anti PatternPassing an object into a method which changes the object, is it a common (anti-) pattern?Efficiency of C# dictionariesA better way than O(n^2) for traversing a dictionary of dictionaries.Dictionary of dictionaries design in C#Does this anti-pattern have a name?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
I find myself often looking up questions online, and many solutions include dictionaries. However, whenever I try to implement them, I get this horrible reek in my code. For example every time I want to use a value:
int x;
if (dict.TryGetValue("key", out x)
DoSomethingWith(x);
That's 4 lines of code to essentially do the following: DoSomethingWith(dict["key"])
I've heard that using the out keyword is an anti pattern because it makes functions mutate their parameters.
Also, I find myself often needing a "reversed" dictionary, where I flip the keys and values.
Similarly, I often would like to iterate through the items in a dictionary and find myself converting keys or values to lists etc to do this better.
I feel like there's almost always a better, more elegant way of using dictionaries, But I'm at a loss.
c# anti-patterns code-smell dictionary out-parameters
add a comment |
I find myself often looking up questions online, and many solutions include dictionaries. However, whenever I try to implement them, I get this horrible reek in my code. For example every time I want to use a value:
int x;
if (dict.TryGetValue("key", out x)
DoSomethingWith(x);
That's 4 lines of code to essentially do the following: DoSomethingWith(dict["key"])
I've heard that using the out keyword is an anti pattern because it makes functions mutate their parameters.
Also, I find myself often needing a "reversed" dictionary, where I flip the keys and values.
Similarly, I often would like to iterate through the items in a dictionary and find myself converting keys or values to lists etc to do this better.
I feel like there's almost always a better, more elegant way of using dictionaries, But I'm at a loss.
c# anti-patterns code-smell dictionary out-parameters
2
Other ways may exist but I generally use a ContainsKey first before trying to get a value.
– Robbie Dee
8 hours ago
IfDoSomethingWith(x)
were an extension method you could technically do:dict.TryGetValue("key", out int x); x?.DoSomething();
Handles the case where "key" does not exist, and still does something with x like the original code did. But Dictionary itself is not an antipattern. It's a collection, so its all about how you use it.
– Berin Loritsch
8 hours ago
Honestly, with a few exceptions, if you know what your dictionary keys are ahead of time, there probably is a better way. If you don't know, the dict keys shouldn't generally be hard-coded at all. Dictionaries are useful for working with semi-structured objects or data where the fields are at least mostly orthogonal to the application. IMO, the closer those fields get to being relevant business/domain concepts, the less helpful dictionaries become for working with those concepts.
– svidgen
7 hours ago
2
@RobbieDee: you have to be careful when you do that though, as doing so creates a race condition. It's possible the key could be removed between calling ContainsKey and getting the value.
– whatsisname
6 hours ago
3
@whatsisname: Under those conditions, a ConcurrentDictionary would be more suitable. Collections in theSystem.Collections.Generic
namespace are not thread-safe.
– Robert Harvey♦
3 hours ago
add a comment |
I find myself often looking up questions online, and many solutions include dictionaries. However, whenever I try to implement them, I get this horrible reek in my code. For example every time I want to use a value:
int x;
if (dict.TryGetValue("key", out x)
DoSomethingWith(x);
That's 4 lines of code to essentially do the following: DoSomethingWith(dict["key"])
I've heard that using the out keyword is an anti pattern because it makes functions mutate their parameters.
Also, I find myself often needing a "reversed" dictionary, where I flip the keys and values.
Similarly, I often would like to iterate through the items in a dictionary and find myself converting keys or values to lists etc to do this better.
I feel like there's almost always a better, more elegant way of using dictionaries, But I'm at a loss.
c# anti-patterns code-smell dictionary out-parameters
I find myself often looking up questions online, and many solutions include dictionaries. However, whenever I try to implement them, I get this horrible reek in my code. For example every time I want to use a value:
int x;
if (dict.TryGetValue("key", out x)
DoSomethingWith(x);
That's 4 lines of code to essentially do the following: DoSomethingWith(dict["key"])
I've heard that using the out keyword is an anti pattern because it makes functions mutate their parameters.
Also, I find myself often needing a "reversed" dictionary, where I flip the keys and values.
Similarly, I often would like to iterate through the items in a dictionary and find myself converting keys or values to lists etc to do this better.
I feel like there's almost always a better, more elegant way of using dictionaries, But I'm at a loss.
c# anti-patterns code-smell dictionary out-parameters
c# anti-patterns code-smell dictionary out-parameters
edited 8 hours ago
Robbie Dee
8,3712 gold badges16 silver badges48 bronze badges
8,3712 gold badges16 silver badges48 bronze badges
asked 8 hours ago
Adam BAdam B
2141 silver badge10 bronze badges
2141 silver badge10 bronze badges
2
Other ways may exist but I generally use a ContainsKey first before trying to get a value.
– Robbie Dee
8 hours ago
IfDoSomethingWith(x)
were an extension method you could technically do:dict.TryGetValue("key", out int x); x?.DoSomething();
Handles the case where "key" does not exist, and still does something with x like the original code did. But Dictionary itself is not an antipattern. It's a collection, so its all about how you use it.
– Berin Loritsch
8 hours ago
Honestly, with a few exceptions, if you know what your dictionary keys are ahead of time, there probably is a better way. If you don't know, the dict keys shouldn't generally be hard-coded at all. Dictionaries are useful for working with semi-structured objects or data where the fields are at least mostly orthogonal to the application. IMO, the closer those fields get to being relevant business/domain concepts, the less helpful dictionaries become for working with those concepts.
– svidgen
7 hours ago
2
@RobbieDee: you have to be careful when you do that though, as doing so creates a race condition. It's possible the key could be removed between calling ContainsKey and getting the value.
– whatsisname
6 hours ago
3
@whatsisname: Under those conditions, a ConcurrentDictionary would be more suitable. Collections in theSystem.Collections.Generic
namespace are not thread-safe.
– Robert Harvey♦
3 hours ago
add a comment |
2
Other ways may exist but I generally use a ContainsKey first before trying to get a value.
– Robbie Dee
8 hours ago
IfDoSomethingWith(x)
were an extension method you could technically do:dict.TryGetValue("key", out int x); x?.DoSomething();
Handles the case where "key" does not exist, and still does something with x like the original code did. But Dictionary itself is not an antipattern. It's a collection, so its all about how you use it.
– Berin Loritsch
8 hours ago
Honestly, with a few exceptions, if you know what your dictionary keys are ahead of time, there probably is a better way. If you don't know, the dict keys shouldn't generally be hard-coded at all. Dictionaries are useful for working with semi-structured objects or data where the fields are at least mostly orthogonal to the application. IMO, the closer those fields get to being relevant business/domain concepts, the less helpful dictionaries become for working with those concepts.
– svidgen
7 hours ago
2
@RobbieDee: you have to be careful when you do that though, as doing so creates a race condition. It's possible the key could be removed between calling ContainsKey and getting the value.
– whatsisname
6 hours ago
3
@whatsisname: Under those conditions, a ConcurrentDictionary would be more suitable. Collections in theSystem.Collections.Generic
namespace are not thread-safe.
– Robert Harvey♦
3 hours ago
2
2
Other ways may exist but I generally use a ContainsKey first before trying to get a value.
– Robbie Dee
8 hours ago
Other ways may exist but I generally use a ContainsKey first before trying to get a value.
– Robbie Dee
8 hours ago
If
DoSomethingWith(x)
were an extension method you could technically do: dict.TryGetValue("key", out int x); x?.DoSomething();
Handles the case where "key" does not exist, and still does something with x like the original code did. But Dictionary itself is not an antipattern. It's a collection, so its all about how you use it.– Berin Loritsch
8 hours ago
If
DoSomethingWith(x)
were an extension method you could technically do: dict.TryGetValue("key", out int x); x?.DoSomething();
Handles the case where "key" does not exist, and still does something with x like the original code did. But Dictionary itself is not an antipattern. It's a collection, so its all about how you use it.– Berin Loritsch
8 hours ago
Honestly, with a few exceptions, if you know what your dictionary keys are ahead of time, there probably is a better way. If you don't know, the dict keys shouldn't generally be hard-coded at all. Dictionaries are useful for working with semi-structured objects or data where the fields are at least mostly orthogonal to the application. IMO, the closer those fields get to being relevant business/domain concepts, the less helpful dictionaries become for working with those concepts.
– svidgen
7 hours ago
Honestly, with a few exceptions, if you know what your dictionary keys are ahead of time, there probably is a better way. If you don't know, the dict keys shouldn't generally be hard-coded at all. Dictionaries are useful for working with semi-structured objects or data where the fields are at least mostly orthogonal to the application. IMO, the closer those fields get to being relevant business/domain concepts, the less helpful dictionaries become for working with those concepts.
– svidgen
7 hours ago
2
2
@RobbieDee: you have to be careful when you do that though, as doing so creates a race condition. It's possible the key could be removed between calling ContainsKey and getting the value.
– whatsisname
6 hours ago
@RobbieDee: you have to be careful when you do that though, as doing so creates a race condition. It's possible the key could be removed between calling ContainsKey and getting the value.
– whatsisname
6 hours ago
3
3
@whatsisname: Under those conditions, a ConcurrentDictionary would be more suitable. Collections in the
System.Collections.Generic
namespace are not thread-safe.– Robert Harvey♦
3 hours ago
@whatsisname: Under those conditions, a ConcurrentDictionary would be more suitable. Collections in the
System.Collections.Generic
namespace are not thread-safe.– Robert Harvey♦
3 hours ago
add a comment |
6 Answers
6
active
oldest
votes
Dictionaries (C# or otherwise) are simply a container where you look up a value based on a key. In many languages it's more correctly identified as a Map with the most common implementation being a HashMap.
The problem to consider is what happens when a key does not exist. Some languages behave by returning null
or nil
or some other equivalent value. Silently defaulting to a value instead of informing you that a value does not exist.
For better or for worse, the C# library designers came up with an idiom to deal with the behavior. They reasoned that the default behavior for looking up a value that does not exist is to throw an exception. If you want to avoid exceptions, then you can use the Try
variant. It's the same approach they use for parsing strings into integers or date/time objects. Essentially, the impact is like this:
T count = int.Parse("12T45"); // throws exception
if (int.TryParse("12T45", out count))
// Does not throw exception
And that carried forward to the dictionary, whose indexer delegates to Get(index)
:
var myvalue = dict["12345"]; // throws exception
myvalue = dict.Get("12345"); // throws exception
if (dict.TryGet("12345", out myvalue))
// Does not throw exception
This is simply the way the language is designed.
Should out
variables be discouraged?
C# isn't the first language to have them, and they have their purpose in specific situations. If you are trying to build a highly concurrent system, then you cannot use out
variables at the concurrency boundaries.
In many ways, if there is an idiom that is espoused by the language and core library providers, I try to adopt those idioms in my APIs. That makes the API feel more consistent and at home in that language. So a method written in Ruby isn't going to look like a method written in C#, C, or Python. They each have a preferred way of building code, and working with that helps the users of your API learn it more quickly.
Are Maps in General an Anti-pattern?
They have their purpose, but many times they may be the wrong solution for the purpose you have. Particularly if you have a bi-directional mapping you need. There are many containers and ways of organizing data. There are many approaches that you can use, and sometimes you need to think a bit before you pick that container.
If you have a very short list of bi-directional mapping values, then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping.
Think of the problem domain, and pick the most appropriate tool for the job. If there isn't one, then create it.
"then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping." -- If there are optimizations for small sets, they should be made in the library, not rubber-stamped into user code.
– Blrfl
3 hours ago
If you choose a list of tuples or a dictionary, that is an implementation detail. It's a question of understanding your problem domain and using the right tool for the job. Obviously, a list exists and a dictionary exists. For the common case, dictionary is correct, but maybe for one or two applications you need to use the list.
– Berin Loritsch
3 hours ago
It is, but if the behavior is still the same, that determination should be in the library. I've run across containers implementations in other languages that will switch algorithms if told a priori that the number of entries will be small. I agree with your answer, but once this has been figured out, it should be in a library, maybe as a SmallBidirectionalMap.
– Blrfl
1 hour ago
add a comment |
This is neither a code smell nor an anti-pattern, as using TryGet-style functions with an out parameter is idiomatic C#. However, there are 3 options provided in C# to work with a Dictionary, so should you be sure you are using the correct one for your situation. I think I know where the rumor of there being a problem using an out parameter comes from, so I'll handle that at the end.
What features to use when working with a C# Dictionary:
- If you are sure the key will be in the Dictionary, use the Item[TKey] property
- If the key should generally be in the Dictionary, but it is bad/rare/problematic that it is not there, you should use Try...Catch so that an error will be raised and then you can try to handle the error gracefully
- If you are not sure the key will be in the Dictionary, use TryGet with the out parameter
To justify this, one need only refer to the documentation for Dictionary TryGetValue, under "Remarks":
This method combines the functionality of the ContainsKey method and the Item[TKey] property.
...
Use the TryGetValue method if your code frequently attempts to access
keys that are not in the dictionary. Using this method is more
efficient than catching the KeyNotFoundException thrown by the
Item[TKey] property.
This method approaches an O(1) operation.
The whole reason TryGetValue exists is to act as a more convenient way to use ContainsKey and Item[TKey], while avoiding having to search the dictionary twice - so pretending it doesn't exist and doing the two things it does manually is a rather awkward choice.
In practice, I rarely have ever used a raw Dictionary, due to this simple maxim: choose the most generic class/container that gives you the functionality you need. Dictionary was not designed to look up by value rather than by key (for example), so if that is something you want it may make more sense to use an alternate structure. I think I may have used Dictionary one time in the last year-long development project I did, simply because it was rarely the right tool for the job I was trying to do. Dictionary is certainly not the Swiss Army knife of the C# toolbox.
What's wrong with out parameters?
CA1021: Avoid out parameters
Although return values are commonplace and heavily used, the correct
application of out and ref parameters requires intermediate design and
coding skills. Library architects who design for a general audience
should not expect users to master working with out or ref parameters.
I'm guessing that's where you heard that out parameters were something like an anti-pattern. As with all rules, you should read closer to understand the 'why', and in this case there is even an explicit mention of how the Try pattern does not violate the rule:
Methods that implement the Try pattern, such as
System.Int32.TryParse, do not raise this violation.
add a comment |
Some good answers here on the general principles of hashtables/dictionaries. But I thought I'd touch on your code example,
int x;
if (dict.TryGetValue("key", out x)
DoSomethingWith(x);
As of C# 7 (which I think is around two years old), that can be simplified to:
if (dict.TryGetValue("key", out var x)
DoSomethingWith(x);
And of course it could be reduced to one line:
if (dict.TryGetValue("key", out var x) DoSomethingWith(x);
If you have a default value for when the key doesn't exist, it can become:
DoSomethingWith(dict.TryGetValue("key", out var x) ? x : defaultValue);
So you can achieve compact forms by using reasonably recent language additions.
1
Good call on the v7 syntax, nice little option to have to cut out the extra definition line while allowing the use of var +1
– BrianH
34 mins ago
add a comment |
The TryGetValue()
construct is only necessary if you don't know whether "key" is present as a key within the dictionary or not, otherwise DoSomethingWith(dict["key"])
is perfectly valid.
A "less dirty" approach might be to use ContainsKey()
as a check instead.
2
"A "less dirty" approach might be to use ContainsKey() as a check instead." I disagree. As suboptimal asTryGetValue
is, at least it makes it hard to forget to handle the empty case. Ideally, I wish this would just return an Optional.
– Alexander
7 hours ago
add a comment |
There are at least two methods missing from C# dictionaries that in my opinion clean up code considerably in a lot of situations in other languages. The first is returning an Option
, which lets you write code like the following in Scala:
dict.get("key").map(doSomethingWith)
The second is returning a user-specified default value if the key isn't found:
doSomethingWith(dict.getOrElse("key", "key not found"))
There is something to be said for using the idioms a language provides when appropriate, like the Try
pattern, but that doesn't mean you have to only use what the language provides. We're programmers. It's okay to create new abstractions to make our specific situation easier to understand, especially if it eliminates a lot of repetition. If you frequently need something, like reverse lookups or iterating through values, make it happen. Create the interface you wish you had.
I like the 2nd option. Maybe I'll have to write an extension method or two :)
– Adam B
7 hours ago
add a comment |
IMHO form a purely theoretical point of view, TryGetValue
and other ThisAndThat
methods is a code smell by itself, it is a method/function that does more than one thing. If methods/functions were composable you would use one to get the value and one to decide what to replace the null
with or to handle the exception. The API is flawed in 2019. But it is what it is.
The currently popular concept of Optional
wrappers in every OO language is a more modern but just as flawed solution as it just moves the problem somewhere else.
There are much better design approaches
Validate the map/dictionary has the keys that are required in another method/function that is a precondition and do it once, and throw an exception that is basically an illegal argument mandatory key is missing with the name of the key so everyone knows exactly how to fix the problem.
Provide a DEFAULT map/dictionary that you copy the values from, then update that with whatever you are putting in there and you always are sure that the keys exist with a proper default and you can just let errors happen because they are indeed exceptional. Or use the DEFAULT map as an overlay map to fill in a sparse one with required keys with the proper default values.
The goal is to just avoid that TryGetValue
mess, it is a code smell because it does more than one thing at time.
add a comment |
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "131"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: false,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f396567%2fare-c-dictionaries-an-anti-pattern%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
StackExchange.ready(function ()
$("#show-editor-button input, #show-editor-button button").click(function ()
var showEditor = function()
$("#show-editor-button").addClass("d-none");
$("#post-form").removeClass("d-none");
StackExchange.editor.finallyInit();
;
var useFancy = $(this).data('confirm-use-fancy');
if(useFancy == 'True')
var popupTitle = $(this).data('confirm-fancy-title');
var popupBody = $(this).data('confirm-fancy-body');
var popupAccept = $(this).data('confirm-fancy-accept-button');
$(this).loadPopup(
url: '/post/self-answer-popup',
loaded: function(popup)
var pTitle = $(popup).find('h2');
var pBody = $(popup).find('.popup-body');
var pSubmit = $(popup).find('.popup-submit');
pTitle.text(popupTitle);
pBody.html(popupBody);
pSubmit.val(popupAccept).click(showEditor);
)
else
var confirmText = $(this).data('confirm-text');
if (confirmText ? confirm(confirmText) : true)
showEditor();
);
);
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
Dictionaries (C# or otherwise) are simply a container where you look up a value based on a key. In many languages it's more correctly identified as a Map with the most common implementation being a HashMap.
The problem to consider is what happens when a key does not exist. Some languages behave by returning null
or nil
or some other equivalent value. Silently defaulting to a value instead of informing you that a value does not exist.
For better or for worse, the C# library designers came up with an idiom to deal with the behavior. They reasoned that the default behavior for looking up a value that does not exist is to throw an exception. If you want to avoid exceptions, then you can use the Try
variant. It's the same approach they use for parsing strings into integers or date/time objects. Essentially, the impact is like this:
T count = int.Parse("12T45"); // throws exception
if (int.TryParse("12T45", out count))
// Does not throw exception
And that carried forward to the dictionary, whose indexer delegates to Get(index)
:
var myvalue = dict["12345"]; // throws exception
myvalue = dict.Get("12345"); // throws exception
if (dict.TryGet("12345", out myvalue))
// Does not throw exception
This is simply the way the language is designed.
Should out
variables be discouraged?
C# isn't the first language to have them, and they have their purpose in specific situations. If you are trying to build a highly concurrent system, then you cannot use out
variables at the concurrency boundaries.
In many ways, if there is an idiom that is espoused by the language and core library providers, I try to adopt those idioms in my APIs. That makes the API feel more consistent and at home in that language. So a method written in Ruby isn't going to look like a method written in C#, C, or Python. They each have a preferred way of building code, and working with that helps the users of your API learn it more quickly.
Are Maps in General an Anti-pattern?
They have their purpose, but many times they may be the wrong solution for the purpose you have. Particularly if you have a bi-directional mapping you need. There are many containers and ways of organizing data. There are many approaches that you can use, and sometimes you need to think a bit before you pick that container.
If you have a very short list of bi-directional mapping values, then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping.
Think of the problem domain, and pick the most appropriate tool for the job. If there isn't one, then create it.
"then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping." -- If there are optimizations for small sets, they should be made in the library, not rubber-stamped into user code.
– Blrfl
3 hours ago
If you choose a list of tuples or a dictionary, that is an implementation detail. It's a question of understanding your problem domain and using the right tool for the job. Obviously, a list exists and a dictionary exists. For the common case, dictionary is correct, but maybe for one or two applications you need to use the list.
– Berin Loritsch
3 hours ago
It is, but if the behavior is still the same, that determination should be in the library. I've run across containers implementations in other languages that will switch algorithms if told a priori that the number of entries will be small. I agree with your answer, but once this has been figured out, it should be in a library, maybe as a SmallBidirectionalMap.
– Blrfl
1 hour ago
add a comment |
Dictionaries (C# or otherwise) are simply a container where you look up a value based on a key. In many languages it's more correctly identified as a Map with the most common implementation being a HashMap.
The problem to consider is what happens when a key does not exist. Some languages behave by returning null
or nil
or some other equivalent value. Silently defaulting to a value instead of informing you that a value does not exist.
For better or for worse, the C# library designers came up with an idiom to deal with the behavior. They reasoned that the default behavior for looking up a value that does not exist is to throw an exception. If you want to avoid exceptions, then you can use the Try
variant. It's the same approach they use for parsing strings into integers or date/time objects. Essentially, the impact is like this:
T count = int.Parse("12T45"); // throws exception
if (int.TryParse("12T45", out count))
// Does not throw exception
And that carried forward to the dictionary, whose indexer delegates to Get(index)
:
var myvalue = dict["12345"]; // throws exception
myvalue = dict.Get("12345"); // throws exception
if (dict.TryGet("12345", out myvalue))
// Does not throw exception
This is simply the way the language is designed.
Should out
variables be discouraged?
C# isn't the first language to have them, and they have their purpose in specific situations. If you are trying to build a highly concurrent system, then you cannot use out
variables at the concurrency boundaries.
In many ways, if there is an idiom that is espoused by the language and core library providers, I try to adopt those idioms in my APIs. That makes the API feel more consistent and at home in that language. So a method written in Ruby isn't going to look like a method written in C#, C, or Python. They each have a preferred way of building code, and working with that helps the users of your API learn it more quickly.
Are Maps in General an Anti-pattern?
They have their purpose, but many times they may be the wrong solution for the purpose you have. Particularly if you have a bi-directional mapping you need. There are many containers and ways of organizing data. There are many approaches that you can use, and sometimes you need to think a bit before you pick that container.
If you have a very short list of bi-directional mapping values, then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping.
Think of the problem domain, and pick the most appropriate tool for the job. If there isn't one, then create it.
"then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping." -- If there are optimizations for small sets, they should be made in the library, not rubber-stamped into user code.
– Blrfl
3 hours ago
If you choose a list of tuples or a dictionary, that is an implementation detail. It's a question of understanding your problem domain and using the right tool for the job. Obviously, a list exists and a dictionary exists. For the common case, dictionary is correct, but maybe for one or two applications you need to use the list.
– Berin Loritsch
3 hours ago
It is, but if the behavior is still the same, that determination should be in the library. I've run across containers implementations in other languages that will switch algorithms if told a priori that the number of entries will be small. I agree with your answer, but once this has been figured out, it should be in a library, maybe as a SmallBidirectionalMap.
– Blrfl
1 hour ago
add a comment |
Dictionaries (C# or otherwise) are simply a container where you look up a value based on a key. In many languages it's more correctly identified as a Map with the most common implementation being a HashMap.
The problem to consider is what happens when a key does not exist. Some languages behave by returning null
or nil
or some other equivalent value. Silently defaulting to a value instead of informing you that a value does not exist.
For better or for worse, the C# library designers came up with an idiom to deal with the behavior. They reasoned that the default behavior for looking up a value that does not exist is to throw an exception. If you want to avoid exceptions, then you can use the Try
variant. It's the same approach they use for parsing strings into integers or date/time objects. Essentially, the impact is like this:
T count = int.Parse("12T45"); // throws exception
if (int.TryParse("12T45", out count))
// Does not throw exception
And that carried forward to the dictionary, whose indexer delegates to Get(index)
:
var myvalue = dict["12345"]; // throws exception
myvalue = dict.Get("12345"); // throws exception
if (dict.TryGet("12345", out myvalue))
// Does not throw exception
This is simply the way the language is designed.
Should out
variables be discouraged?
C# isn't the first language to have them, and they have their purpose in specific situations. If you are trying to build a highly concurrent system, then you cannot use out
variables at the concurrency boundaries.
In many ways, if there is an idiom that is espoused by the language and core library providers, I try to adopt those idioms in my APIs. That makes the API feel more consistent and at home in that language. So a method written in Ruby isn't going to look like a method written in C#, C, or Python. They each have a preferred way of building code, and working with that helps the users of your API learn it more quickly.
Are Maps in General an Anti-pattern?
They have their purpose, but many times they may be the wrong solution for the purpose you have. Particularly if you have a bi-directional mapping you need. There are many containers and ways of organizing data. There are many approaches that you can use, and sometimes you need to think a bit before you pick that container.
If you have a very short list of bi-directional mapping values, then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping.
Think of the problem domain, and pick the most appropriate tool for the job. If there isn't one, then create it.
Dictionaries (C# or otherwise) are simply a container where you look up a value based on a key. In many languages it's more correctly identified as a Map with the most common implementation being a HashMap.
The problem to consider is what happens when a key does not exist. Some languages behave by returning null
or nil
or some other equivalent value. Silently defaulting to a value instead of informing you that a value does not exist.
For better or for worse, the C# library designers came up with an idiom to deal with the behavior. They reasoned that the default behavior for looking up a value that does not exist is to throw an exception. If you want to avoid exceptions, then you can use the Try
variant. It's the same approach they use for parsing strings into integers or date/time objects. Essentially, the impact is like this:
T count = int.Parse("12T45"); // throws exception
if (int.TryParse("12T45", out count))
// Does not throw exception
And that carried forward to the dictionary, whose indexer delegates to Get(index)
:
var myvalue = dict["12345"]; // throws exception
myvalue = dict.Get("12345"); // throws exception
if (dict.TryGet("12345", out myvalue))
// Does not throw exception
This is simply the way the language is designed.
Should out
variables be discouraged?
C# isn't the first language to have them, and they have their purpose in specific situations. If you are trying to build a highly concurrent system, then you cannot use out
variables at the concurrency boundaries.
In many ways, if there is an idiom that is espoused by the language and core library providers, I try to adopt those idioms in my APIs. That makes the API feel more consistent and at home in that language. So a method written in Ruby isn't going to look like a method written in C#, C, or Python. They each have a preferred way of building code, and working with that helps the users of your API learn it more quickly.
Are Maps in General an Anti-pattern?
They have their purpose, but many times they may be the wrong solution for the purpose you have. Particularly if you have a bi-directional mapping you need. There are many containers and ways of organizing data. There are many approaches that you can use, and sometimes you need to think a bit before you pick that container.
If you have a very short list of bi-directional mapping values, then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping.
Think of the problem domain, and pick the most appropriate tool for the job. If there isn't one, then create it.
answered 8 hours ago
Berin LoritschBerin Loritsch
35.7k5 gold badges66 silver badges140 bronze badges
35.7k5 gold badges66 silver badges140 bronze badges
"then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping." -- If there are optimizations for small sets, they should be made in the library, not rubber-stamped into user code.
– Blrfl
3 hours ago
If you choose a list of tuples or a dictionary, that is an implementation detail. It's a question of understanding your problem domain and using the right tool for the job. Obviously, a list exists and a dictionary exists. For the common case, dictionary is correct, but maybe for one or two applications you need to use the list.
– Berin Loritsch
3 hours ago
It is, but if the behavior is still the same, that determination should be in the library. I've run across containers implementations in other languages that will switch algorithms if told a priori that the number of entries will be small. I agree with your answer, but once this has been figured out, it should be in a library, maybe as a SmallBidirectionalMap.
– Blrfl
1 hour ago
add a comment |
"then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping." -- If there are optimizations for small sets, they should be made in the library, not rubber-stamped into user code.
– Blrfl
3 hours ago
If you choose a list of tuples or a dictionary, that is an implementation detail. It's a question of understanding your problem domain and using the right tool for the job. Obviously, a list exists and a dictionary exists. For the common case, dictionary is correct, but maybe for one or two applications you need to use the list.
– Berin Loritsch
3 hours ago
It is, but if the behavior is still the same, that determination should be in the library. I've run across containers implementations in other languages that will switch algorithms if told a priori that the number of entries will be small. I agree with your answer, but once this has been figured out, it should be in a library, maybe as a SmallBidirectionalMap.
– Blrfl
1 hour ago
"then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping." -- If there are optimizations for small sets, they should be made in the library, not rubber-stamped into user code.
– Blrfl
3 hours ago
"then you might only need a list of tuples. Or a list of structs, where you can just as easily find the first match on either side of the mapping." -- If there are optimizations for small sets, they should be made in the library, not rubber-stamped into user code.
– Blrfl
3 hours ago
If you choose a list of tuples or a dictionary, that is an implementation detail. It's a question of understanding your problem domain and using the right tool for the job. Obviously, a list exists and a dictionary exists. For the common case, dictionary is correct, but maybe for one or two applications you need to use the list.
– Berin Loritsch
3 hours ago
If you choose a list of tuples or a dictionary, that is an implementation detail. It's a question of understanding your problem domain and using the right tool for the job. Obviously, a list exists and a dictionary exists. For the common case, dictionary is correct, but maybe for one or two applications you need to use the list.
– Berin Loritsch
3 hours ago
It is, but if the behavior is still the same, that determination should be in the library. I've run across containers implementations in other languages that will switch algorithms if told a priori that the number of entries will be small. I agree with your answer, but once this has been figured out, it should be in a library, maybe as a SmallBidirectionalMap.
– Blrfl
1 hour ago
It is, but if the behavior is still the same, that determination should be in the library. I've run across containers implementations in other languages that will switch algorithms if told a priori that the number of entries will be small. I agree with your answer, but once this has been figured out, it should be in a library, maybe as a SmallBidirectionalMap.
– Blrfl
1 hour ago
add a comment |
This is neither a code smell nor an anti-pattern, as using TryGet-style functions with an out parameter is idiomatic C#. However, there are 3 options provided in C# to work with a Dictionary, so should you be sure you are using the correct one for your situation. I think I know where the rumor of there being a problem using an out parameter comes from, so I'll handle that at the end.
What features to use when working with a C# Dictionary:
- If you are sure the key will be in the Dictionary, use the Item[TKey] property
- If the key should generally be in the Dictionary, but it is bad/rare/problematic that it is not there, you should use Try...Catch so that an error will be raised and then you can try to handle the error gracefully
- If you are not sure the key will be in the Dictionary, use TryGet with the out parameter
To justify this, one need only refer to the documentation for Dictionary TryGetValue, under "Remarks":
This method combines the functionality of the ContainsKey method and the Item[TKey] property.
...
Use the TryGetValue method if your code frequently attempts to access
keys that are not in the dictionary. Using this method is more
efficient than catching the KeyNotFoundException thrown by the
Item[TKey] property.
This method approaches an O(1) operation.
The whole reason TryGetValue exists is to act as a more convenient way to use ContainsKey and Item[TKey], while avoiding having to search the dictionary twice - so pretending it doesn't exist and doing the two things it does manually is a rather awkward choice.
In practice, I rarely have ever used a raw Dictionary, due to this simple maxim: choose the most generic class/container that gives you the functionality you need. Dictionary was not designed to look up by value rather than by key (for example), so if that is something you want it may make more sense to use an alternate structure. I think I may have used Dictionary one time in the last year-long development project I did, simply because it was rarely the right tool for the job I was trying to do. Dictionary is certainly not the Swiss Army knife of the C# toolbox.
What's wrong with out parameters?
CA1021: Avoid out parameters
Although return values are commonplace and heavily used, the correct
application of out and ref parameters requires intermediate design and
coding skills. Library architects who design for a general audience
should not expect users to master working with out or ref parameters.
I'm guessing that's where you heard that out parameters were something like an anti-pattern. As with all rules, you should read closer to understand the 'why', and in this case there is even an explicit mention of how the Try pattern does not violate the rule:
Methods that implement the Try pattern, such as
System.Int32.TryParse, do not raise this violation.
add a comment |
This is neither a code smell nor an anti-pattern, as using TryGet-style functions with an out parameter is idiomatic C#. However, there are 3 options provided in C# to work with a Dictionary, so should you be sure you are using the correct one for your situation. I think I know where the rumor of there being a problem using an out parameter comes from, so I'll handle that at the end.
What features to use when working with a C# Dictionary:
- If you are sure the key will be in the Dictionary, use the Item[TKey] property
- If the key should generally be in the Dictionary, but it is bad/rare/problematic that it is not there, you should use Try...Catch so that an error will be raised and then you can try to handle the error gracefully
- If you are not sure the key will be in the Dictionary, use TryGet with the out parameter
To justify this, one need only refer to the documentation for Dictionary TryGetValue, under "Remarks":
This method combines the functionality of the ContainsKey method and the Item[TKey] property.
...
Use the TryGetValue method if your code frequently attempts to access
keys that are not in the dictionary. Using this method is more
efficient than catching the KeyNotFoundException thrown by the
Item[TKey] property.
This method approaches an O(1) operation.
The whole reason TryGetValue exists is to act as a more convenient way to use ContainsKey and Item[TKey], while avoiding having to search the dictionary twice - so pretending it doesn't exist and doing the two things it does manually is a rather awkward choice.
In practice, I rarely have ever used a raw Dictionary, due to this simple maxim: choose the most generic class/container that gives you the functionality you need. Dictionary was not designed to look up by value rather than by key (for example), so if that is something you want it may make more sense to use an alternate structure. I think I may have used Dictionary one time in the last year-long development project I did, simply because it was rarely the right tool for the job I was trying to do. Dictionary is certainly not the Swiss Army knife of the C# toolbox.
What's wrong with out parameters?
CA1021: Avoid out parameters
Although return values are commonplace and heavily used, the correct
application of out and ref parameters requires intermediate design and
coding skills. Library architects who design for a general audience
should not expect users to master working with out or ref parameters.
I'm guessing that's where you heard that out parameters were something like an anti-pattern. As with all rules, you should read closer to understand the 'why', and in this case there is even an explicit mention of how the Try pattern does not violate the rule:
Methods that implement the Try pattern, such as
System.Int32.TryParse, do not raise this violation.
add a comment |
This is neither a code smell nor an anti-pattern, as using TryGet-style functions with an out parameter is idiomatic C#. However, there are 3 options provided in C# to work with a Dictionary, so should you be sure you are using the correct one for your situation. I think I know where the rumor of there being a problem using an out parameter comes from, so I'll handle that at the end.
What features to use when working with a C# Dictionary:
- If you are sure the key will be in the Dictionary, use the Item[TKey] property
- If the key should generally be in the Dictionary, but it is bad/rare/problematic that it is not there, you should use Try...Catch so that an error will be raised and then you can try to handle the error gracefully
- If you are not sure the key will be in the Dictionary, use TryGet with the out parameter
To justify this, one need only refer to the documentation for Dictionary TryGetValue, under "Remarks":
This method combines the functionality of the ContainsKey method and the Item[TKey] property.
...
Use the TryGetValue method if your code frequently attempts to access
keys that are not in the dictionary. Using this method is more
efficient than catching the KeyNotFoundException thrown by the
Item[TKey] property.
This method approaches an O(1) operation.
The whole reason TryGetValue exists is to act as a more convenient way to use ContainsKey and Item[TKey], while avoiding having to search the dictionary twice - so pretending it doesn't exist and doing the two things it does manually is a rather awkward choice.
In practice, I rarely have ever used a raw Dictionary, due to this simple maxim: choose the most generic class/container that gives you the functionality you need. Dictionary was not designed to look up by value rather than by key (for example), so if that is something you want it may make more sense to use an alternate structure. I think I may have used Dictionary one time in the last year-long development project I did, simply because it was rarely the right tool for the job I was trying to do. Dictionary is certainly not the Swiss Army knife of the C# toolbox.
What's wrong with out parameters?
CA1021: Avoid out parameters
Although return values are commonplace and heavily used, the correct
application of out and ref parameters requires intermediate design and
coding skills. Library architects who design for a general audience
should not expect users to master working with out or ref parameters.
I'm guessing that's where you heard that out parameters were something like an anti-pattern. As with all rules, you should read closer to understand the 'why', and in this case there is even an explicit mention of how the Try pattern does not violate the rule:
Methods that implement the Try pattern, such as
System.Int32.TryParse, do not raise this violation.
This is neither a code smell nor an anti-pattern, as using TryGet-style functions with an out parameter is idiomatic C#. However, there are 3 options provided in C# to work with a Dictionary, so should you be sure you are using the correct one for your situation. I think I know where the rumor of there being a problem using an out parameter comes from, so I'll handle that at the end.
What features to use when working with a C# Dictionary:
- If you are sure the key will be in the Dictionary, use the Item[TKey] property
- If the key should generally be in the Dictionary, but it is bad/rare/problematic that it is not there, you should use Try...Catch so that an error will be raised and then you can try to handle the error gracefully
- If you are not sure the key will be in the Dictionary, use TryGet with the out parameter
To justify this, one need only refer to the documentation for Dictionary TryGetValue, under "Remarks":
This method combines the functionality of the ContainsKey method and the Item[TKey] property.
...
Use the TryGetValue method if your code frequently attempts to access
keys that are not in the dictionary. Using this method is more
efficient than catching the KeyNotFoundException thrown by the
Item[TKey] property.
This method approaches an O(1) operation.
The whole reason TryGetValue exists is to act as a more convenient way to use ContainsKey and Item[TKey], while avoiding having to search the dictionary twice - so pretending it doesn't exist and doing the two things it does manually is a rather awkward choice.
In practice, I rarely have ever used a raw Dictionary, due to this simple maxim: choose the most generic class/container that gives you the functionality you need. Dictionary was not designed to look up by value rather than by key (for example), so if that is something you want it may make more sense to use an alternate structure. I think I may have used Dictionary one time in the last year-long development project I did, simply because it was rarely the right tool for the job I was trying to do. Dictionary is certainly not the Swiss Army knife of the C# toolbox.
What's wrong with out parameters?
CA1021: Avoid out parameters
Although return values are commonplace and heavily used, the correct
application of out and ref parameters requires intermediate design and
coding skills. Library architects who design for a general audience
should not expect users to master working with out or ref parameters.
I'm guessing that's where you heard that out parameters were something like an anti-pattern. As with all rules, you should read closer to understand the 'why', and in this case there is even an explicit mention of how the Try pattern does not violate the rule:
Methods that implement the Try pattern, such as
System.Int32.TryParse, do not raise this violation.
answered 7 hours ago
BrianHBrianH
5,7281 gold badge17 silver badges22 bronze badges
5,7281 gold badge17 silver badges22 bronze badges
add a comment |
add a comment |
Some good answers here on the general principles of hashtables/dictionaries. But I thought I'd touch on your code example,
int x;
if (dict.TryGetValue("key", out x)
DoSomethingWith(x);
As of C# 7 (which I think is around two years old), that can be simplified to:
if (dict.TryGetValue("key", out var x)
DoSomethingWith(x);
And of course it could be reduced to one line:
if (dict.TryGetValue("key", out var x) DoSomethingWith(x);
If you have a default value for when the key doesn't exist, it can become:
DoSomethingWith(dict.TryGetValue("key", out var x) ? x : defaultValue);
So you can achieve compact forms by using reasonably recent language additions.
1
Good call on the v7 syntax, nice little option to have to cut out the extra definition line while allowing the use of var +1
– BrianH
34 mins ago
add a comment |
Some good answers here on the general principles of hashtables/dictionaries. But I thought I'd touch on your code example,
int x;
if (dict.TryGetValue("key", out x)
DoSomethingWith(x);
As of C# 7 (which I think is around two years old), that can be simplified to:
if (dict.TryGetValue("key", out var x)
DoSomethingWith(x);
And of course it could be reduced to one line:
if (dict.TryGetValue("key", out var x) DoSomethingWith(x);
If you have a default value for when the key doesn't exist, it can become:
DoSomethingWith(dict.TryGetValue("key", out var x) ? x : defaultValue);
So you can achieve compact forms by using reasonably recent language additions.
1
Good call on the v7 syntax, nice little option to have to cut out the extra definition line while allowing the use of var +1
– BrianH
34 mins ago
add a comment |
Some good answers here on the general principles of hashtables/dictionaries. But I thought I'd touch on your code example,
int x;
if (dict.TryGetValue("key", out x)
DoSomethingWith(x);
As of C# 7 (which I think is around two years old), that can be simplified to:
if (dict.TryGetValue("key", out var x)
DoSomethingWith(x);
And of course it could be reduced to one line:
if (dict.TryGetValue("key", out var x) DoSomethingWith(x);
If you have a default value for when the key doesn't exist, it can become:
DoSomethingWith(dict.TryGetValue("key", out var x) ? x : defaultValue);
So you can achieve compact forms by using reasonably recent language additions.
Some good answers here on the general principles of hashtables/dictionaries. But I thought I'd touch on your code example,
int x;
if (dict.TryGetValue("key", out x)
DoSomethingWith(x);
As of C# 7 (which I think is around two years old), that can be simplified to:
if (dict.TryGetValue("key", out var x)
DoSomethingWith(x);
And of course it could be reduced to one line:
if (dict.TryGetValue("key", out var x) DoSomethingWith(x);
If you have a default value for when the key doesn't exist, it can become:
DoSomethingWith(dict.TryGetValue("key", out var x) ? x : defaultValue);
So you can achieve compact forms by using reasonably recent language additions.
answered 4 hours ago
David ArnoDavid Arno
31.9k8 gold badges66 silver badges102 bronze badges
31.9k8 gold badges66 silver badges102 bronze badges
1
Good call on the v7 syntax, nice little option to have to cut out the extra definition line while allowing the use of var +1
– BrianH
34 mins ago
add a comment |
1
Good call on the v7 syntax, nice little option to have to cut out the extra definition line while allowing the use of var +1
– BrianH
34 mins ago
1
1
Good call on the v7 syntax, nice little option to have to cut out the extra definition line while allowing the use of var +1
– BrianH
34 mins ago
Good call on the v7 syntax, nice little option to have to cut out the extra definition line while allowing the use of var +1
– BrianH
34 mins ago
add a comment |
The TryGetValue()
construct is only necessary if you don't know whether "key" is present as a key within the dictionary or not, otherwise DoSomethingWith(dict["key"])
is perfectly valid.
A "less dirty" approach might be to use ContainsKey()
as a check instead.
2
"A "less dirty" approach might be to use ContainsKey() as a check instead." I disagree. As suboptimal asTryGetValue
is, at least it makes it hard to forget to handle the empty case. Ideally, I wish this would just return an Optional.
– Alexander
7 hours ago
add a comment |
The TryGetValue()
construct is only necessary if you don't know whether "key" is present as a key within the dictionary or not, otherwise DoSomethingWith(dict["key"])
is perfectly valid.
A "less dirty" approach might be to use ContainsKey()
as a check instead.
2
"A "less dirty" approach might be to use ContainsKey() as a check instead." I disagree. As suboptimal asTryGetValue
is, at least it makes it hard to forget to handle the empty case. Ideally, I wish this would just return an Optional.
– Alexander
7 hours ago
add a comment |
The TryGetValue()
construct is only necessary if you don't know whether "key" is present as a key within the dictionary or not, otherwise DoSomethingWith(dict["key"])
is perfectly valid.
A "less dirty" approach might be to use ContainsKey()
as a check instead.
The TryGetValue()
construct is only necessary if you don't know whether "key" is present as a key within the dictionary or not, otherwise DoSomethingWith(dict["key"])
is perfectly valid.
A "less dirty" approach might be to use ContainsKey()
as a check instead.
answered 8 hours ago
PeregrinePeregrine
1,1441 gold badge6 silver badges9 bronze badges
1,1441 gold badge6 silver badges9 bronze badges
2
"A "less dirty" approach might be to use ContainsKey() as a check instead." I disagree. As suboptimal asTryGetValue
is, at least it makes it hard to forget to handle the empty case. Ideally, I wish this would just return an Optional.
– Alexander
7 hours ago
add a comment |
2
"A "less dirty" approach might be to use ContainsKey() as a check instead." I disagree. As suboptimal asTryGetValue
is, at least it makes it hard to forget to handle the empty case. Ideally, I wish this would just return an Optional.
– Alexander
7 hours ago
2
2
"A "less dirty" approach might be to use ContainsKey() as a check instead." I disagree. As suboptimal as
TryGetValue
is, at least it makes it hard to forget to handle the empty case. Ideally, I wish this would just return an Optional.– Alexander
7 hours ago
"A "less dirty" approach might be to use ContainsKey() as a check instead." I disagree. As suboptimal as
TryGetValue
is, at least it makes it hard to forget to handle the empty case. Ideally, I wish this would just return an Optional.– Alexander
7 hours ago
add a comment |
There are at least two methods missing from C# dictionaries that in my opinion clean up code considerably in a lot of situations in other languages. The first is returning an Option
, which lets you write code like the following in Scala:
dict.get("key").map(doSomethingWith)
The second is returning a user-specified default value if the key isn't found:
doSomethingWith(dict.getOrElse("key", "key not found"))
There is something to be said for using the idioms a language provides when appropriate, like the Try
pattern, but that doesn't mean you have to only use what the language provides. We're programmers. It's okay to create new abstractions to make our specific situation easier to understand, especially if it eliminates a lot of repetition. If you frequently need something, like reverse lookups or iterating through values, make it happen. Create the interface you wish you had.
I like the 2nd option. Maybe I'll have to write an extension method or two :)
– Adam B
7 hours ago
add a comment |
There are at least two methods missing from C# dictionaries that in my opinion clean up code considerably in a lot of situations in other languages. The first is returning an Option
, which lets you write code like the following in Scala:
dict.get("key").map(doSomethingWith)
The second is returning a user-specified default value if the key isn't found:
doSomethingWith(dict.getOrElse("key", "key not found"))
There is something to be said for using the idioms a language provides when appropriate, like the Try
pattern, but that doesn't mean you have to only use what the language provides. We're programmers. It's okay to create new abstractions to make our specific situation easier to understand, especially if it eliminates a lot of repetition. If you frequently need something, like reverse lookups or iterating through values, make it happen. Create the interface you wish you had.
I like the 2nd option. Maybe I'll have to write an extension method or two :)
– Adam B
7 hours ago
add a comment |
There are at least two methods missing from C# dictionaries that in my opinion clean up code considerably in a lot of situations in other languages. The first is returning an Option
, which lets you write code like the following in Scala:
dict.get("key").map(doSomethingWith)
The second is returning a user-specified default value if the key isn't found:
doSomethingWith(dict.getOrElse("key", "key not found"))
There is something to be said for using the idioms a language provides when appropriate, like the Try
pattern, but that doesn't mean you have to only use what the language provides. We're programmers. It's okay to create new abstractions to make our specific situation easier to understand, especially if it eliminates a lot of repetition. If you frequently need something, like reverse lookups or iterating through values, make it happen. Create the interface you wish you had.
There are at least two methods missing from C# dictionaries that in my opinion clean up code considerably in a lot of situations in other languages. The first is returning an Option
, which lets you write code like the following in Scala:
dict.get("key").map(doSomethingWith)
The second is returning a user-specified default value if the key isn't found:
doSomethingWith(dict.getOrElse("key", "key not found"))
There is something to be said for using the idioms a language provides when appropriate, like the Try
pattern, but that doesn't mean you have to only use what the language provides. We're programmers. It's okay to create new abstractions to make our specific situation easier to understand, especially if it eliminates a lot of repetition. If you frequently need something, like reverse lookups or iterating through values, make it happen. Create the interface you wish you had.
answered 7 hours ago
Karl BielefeldtKarl Bielefeldt
124k34 gold badges228 silver badges425 bronze badges
124k34 gold badges228 silver badges425 bronze badges
I like the 2nd option. Maybe I'll have to write an extension method or two :)
– Adam B
7 hours ago
add a comment |
I like the 2nd option. Maybe I'll have to write an extension method or two :)
– Adam B
7 hours ago
I like the 2nd option. Maybe I'll have to write an extension method or two :)
– Adam B
7 hours ago
I like the 2nd option. Maybe I'll have to write an extension method or two :)
– Adam B
7 hours ago
add a comment |
IMHO form a purely theoretical point of view, TryGetValue
and other ThisAndThat
methods is a code smell by itself, it is a method/function that does more than one thing. If methods/functions were composable you would use one to get the value and one to decide what to replace the null
with or to handle the exception. The API is flawed in 2019. But it is what it is.
The currently popular concept of Optional
wrappers in every OO language is a more modern but just as flawed solution as it just moves the problem somewhere else.
There are much better design approaches
Validate the map/dictionary has the keys that are required in another method/function that is a precondition and do it once, and throw an exception that is basically an illegal argument mandatory key is missing with the name of the key so everyone knows exactly how to fix the problem.
Provide a DEFAULT map/dictionary that you copy the values from, then update that with whatever you are putting in there and you always are sure that the keys exist with a proper default and you can just let errors happen because they are indeed exceptional. Or use the DEFAULT map as an overlay map to fill in a sparse one with required keys with the proper default values.
The goal is to just avoid that TryGetValue
mess, it is a code smell because it does more than one thing at time.
add a comment |
IMHO form a purely theoretical point of view, TryGetValue
and other ThisAndThat
methods is a code smell by itself, it is a method/function that does more than one thing. If methods/functions were composable you would use one to get the value and one to decide what to replace the null
with or to handle the exception. The API is flawed in 2019. But it is what it is.
The currently popular concept of Optional
wrappers in every OO language is a more modern but just as flawed solution as it just moves the problem somewhere else.
There are much better design approaches
Validate the map/dictionary has the keys that are required in another method/function that is a precondition and do it once, and throw an exception that is basically an illegal argument mandatory key is missing with the name of the key so everyone knows exactly how to fix the problem.
Provide a DEFAULT map/dictionary that you copy the values from, then update that with whatever you are putting in there and you always are sure that the keys exist with a proper default and you can just let errors happen because they are indeed exceptional. Or use the DEFAULT map as an overlay map to fill in a sparse one with required keys with the proper default values.
The goal is to just avoid that TryGetValue
mess, it is a code smell because it does more than one thing at time.
add a comment |
IMHO form a purely theoretical point of view, TryGetValue
and other ThisAndThat
methods is a code smell by itself, it is a method/function that does more than one thing. If methods/functions were composable you would use one to get the value and one to decide what to replace the null
with or to handle the exception. The API is flawed in 2019. But it is what it is.
The currently popular concept of Optional
wrappers in every OO language is a more modern but just as flawed solution as it just moves the problem somewhere else.
There are much better design approaches
Validate the map/dictionary has the keys that are required in another method/function that is a precondition and do it once, and throw an exception that is basically an illegal argument mandatory key is missing with the name of the key so everyone knows exactly how to fix the problem.
Provide a DEFAULT map/dictionary that you copy the values from, then update that with whatever you are putting in there and you always are sure that the keys exist with a proper default and you can just let errors happen because they are indeed exceptional. Or use the DEFAULT map as an overlay map to fill in a sparse one with required keys with the proper default values.
The goal is to just avoid that TryGetValue
mess, it is a code smell because it does more than one thing at time.
IMHO form a purely theoretical point of view, TryGetValue
and other ThisAndThat
methods is a code smell by itself, it is a method/function that does more than one thing. If methods/functions were composable you would use one to get the value and one to decide what to replace the null
with or to handle the exception. The API is flawed in 2019. But it is what it is.
The currently popular concept of Optional
wrappers in every OO language is a more modern but just as flawed solution as it just moves the problem somewhere else.
There are much better design approaches
Validate the map/dictionary has the keys that are required in another method/function that is a precondition and do it once, and throw an exception that is basically an illegal argument mandatory key is missing with the name of the key so everyone knows exactly how to fix the problem.
Provide a DEFAULT map/dictionary that you copy the values from, then update that with whatever you are putting in there and you always are sure that the keys exist with a proper default and you can just let errors happen because they are indeed exceptional. Or use the DEFAULT map as an overlay map to fill in a sparse one with required keys with the proper default values.
The goal is to just avoid that TryGetValue
mess, it is a code smell because it does more than one thing at time.
edited 5 hours ago
answered 6 hours ago
Jarrod RobersonJarrod Roberson
20.3k6 gold badges46 silver badges82 bronze badges
20.3k6 gold badges46 silver badges82 bronze badges
add a comment |
add a comment |
Thanks for contributing an answer to Software Engineering Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f396567%2fare-c-dictionaries-an-anti-pattern%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
Other ways may exist but I generally use a ContainsKey first before trying to get a value.
– Robbie Dee
8 hours ago
If
DoSomethingWith(x)
were an extension method you could technically do:dict.TryGetValue("key", out int x); x?.DoSomething();
Handles the case where "key" does not exist, and still does something with x like the original code did. But Dictionary itself is not an antipattern. It's a collection, so its all about how you use it.– Berin Loritsch
8 hours ago
Honestly, with a few exceptions, if you know what your dictionary keys are ahead of time, there probably is a better way. If you don't know, the dict keys shouldn't generally be hard-coded at all. Dictionaries are useful for working with semi-structured objects or data where the fields are at least mostly orthogonal to the application. IMO, the closer those fields get to being relevant business/domain concepts, the less helpful dictionaries become for working with those concepts.
– svidgen
7 hours ago
2
@RobbieDee: you have to be careful when you do that though, as doing so creates a race condition. It's possible the key could be removed between calling ContainsKey and getting the value.
– whatsisname
6 hours ago
3
@whatsisname: Under those conditions, a ConcurrentDictionary would be more suitable. Collections in the
System.Collections.Generic
namespace are not thread-safe.– Robert Harvey♦
3 hours ago