When dealing with an enumeration containing none, one or many of an item the foreach
loop makes it easy to iterate over them and LINQ Select
makes it easy to project items from the enumeration.
foreach (var attr in p.GetCustomAttributes().OfType<A>()) ...
When dealing with an enumeration containing none or just one of an item things are a little trickier. You can use the same foreach
loop but that doesn't ensure that there is only one of the items present and it may execute more than once.
You could add .Take(1)
to make it clear that you only want up to one and to ensure that the loop only executes once. But that isn't going to throw an Exception if there is more than one.
foreach (var attr in ...().Take(1)) ...
You could add a check beforehand to make sure there is only one, or perhaps a new code contract to check, but that's rather messy:
Contract.Requires(p..GetCustomAttributes().OfType<A>().Count() < 2);
foreach (var attr in p.GetCustomAttributes().OfType<A>()) ...
You could even create a new extension method .ZeroOrOne<T>()
which returns the first element in an enumeration and then throws an exception if you ask for more.
foreach (var attr in ...().ZeroOrOne()) ...
Or you can get rid of the loop, use .SingleOrDefault()
and then test for null which is clearly the clearest of all in terms of intent and ensuring that the loop executes zero or one times but again it's more code, it's less 'declarative' and it's not 'composable':
var attr = p.GetCustomAttributes().OfType<开发者_JAVA技巧;A>().SingleOrDefault();
if (attr != null)
{
...
So to the question: Which of the above (or other suggestions) do people prefer and why? Most programmers used to procedural code will naturally prefer the if
statement but for those of you deep into functional programming, do you have a better approach?
Some other languages have clean techniques to handle this, e.g. F#'s array patterns can match zero, one, or more, or fail. Is there a case for a forone
construct in the C# language that would be equivalent to checking that there are zero or one in the enumeration and then executing it?
[The specific example here is getting custom attributes where the attribute has been marked to not allow multiple but the question is more generally about handling the zero/one case.]
Obviously I'll choose SingleOrDefault
because:
(1) It's a built-in method. I don't need to write extra methods myself. Mr. ZeroOrOne<T>
, you are out.
(2) An easy example:
var attr = source.SingleOrDefault();
if (attr != null)
{
//do something to attr
}
else
{
//the source is empty
//throw an exception or prompt the user or something equivalent
}
If you use other solutions, I'm afraid you need to write another if
statement to check source.Count()
. So SingleOrDefault
is the winner.
Edit:
With that clarification, I would favour the ZeroOrOne<T>
extension method inside a loop. It would essentially do what you did with the explicit test for more than one. I am a little torn whether it should return IEnumerable<T>
or Nullable<T>
though. I guess it depends on how you would typically be using the result.
Old Answer
It sounds to me like you want the .Single
extension method rather than .SingleOrDefault
.
You said:
You could add .Take(1) to make it clear that you only want up to one and to ensure that the loop only executes once. But that isn't going to throw an Exception if there is more than one.
What you are describing is .Single
. Plus, it is short and demonstrates very clearly your intent.
var attr = p.GetCustomAttributes().OfType<A>().Single();
// will throw exception if there is more than one attribute of type A
精彩评论