Skip to content

Commit b0dc7ad

Browse files
Atif AzizAtif Aziz
authored andcommitted
Merge branch 'master' into formattable
2 parents 7c5b448 + 61aadc6 commit b0dc7ad

29 files changed

+991
-496
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ _TeamCity*
114114
_NCrunch_*
115115
.*crunch*.local.xml
116116
nCrunchTemp_*
117+
*.ncrunchproject
118+
*.ncrunchsolution
117119

118120
# MightyMoose
119121
*.mm.*

README.md

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Optional is a robust option/maybe type for C#.
44

5-
Version: 3.2.0
5+
Version: 4.0.0
66

77
## What and Why?
88

@@ -137,7 +137,7 @@ var value = option.ValueOr(10);
137137
var value = option.ValueOr(() => SlowOperation()); // Lazy variant
138138
```
139139

140-
In more elobarate scenarios, the `Match` method evaluates a specified function:
140+
In more elaborate scenarios, the `Match` method evaluates a specified function:
141141

142142
```csharp
143143
// Evaluates one of the provided functions and returns the result
@@ -198,6 +198,14 @@ var anotherValue = option.ValueOrFailure("An error message");
198198

199199
In case of failure an `OptionValueMissingException` is thrown.
200200

201+
In a lot of interop scenarios, it might be necessary to convert an option into a potentially null value. Once the Unsafe namespace is imported, this can be done relatively concisely as:
202+
203+
```csharp
204+
var value = option.ValueOrDefault(); // value will be default(T) if the option is empty.
205+
```
206+
207+
As a rule of thumb, such conversions should be performed only just before the nullable value is needed (e.g. passed to an external library), to minimize and localize the potential for null reference exceptions and the like.
208+
201209
### Transforming and filtering values
202210

203211
A few extension methods are provided to safely manipulate optional values.
@@ -286,7 +294,7 @@ An option implements `GetEnumerator`, allowing you to loop over the value, as if
286294
```csharp
287295
foreach (var value in option)
288296
{
289-
Console.WriteLine(value);
297+
Console.WriteLine(value);
290298
}
291299
```
292300

@@ -323,7 +331,7 @@ var personWithGreenHair =
323331

324332
In general, this closely resembles a sequence of calls to `FlatMap` and `Filter`. However, using query syntax can be a lot easier to read in complex cases.
325333

326-
### Equivalence
334+
### Equivalence and comparison
327335

328336
Two optional values are equal if the following is satisfied:
329337

@@ -334,6 +342,11 @@ An option both overrides `object.Equals` and implements `IEquatable<T>`, allowin
334342

335343
The generated hashcodes also reflect the semantics described above.
336344

345+
Further, options implement `IComparable<T>` and overload the corresponding comparison operators (`< > <= >=`). The implementation is consistent with the above described equality semantics, and comparison itself is based on the following rules:
346+
347+
* An empty option is considered less than a non-empty option
348+
* For non-empty options comparison is delegated to the default comparer and applied on the contained value
349+
337350
### Options with exceptional values
338351

339352
As described above, Optional support the notion of an either type, which adds and exception value, indicating how an operation went wrong.
@@ -402,6 +415,7 @@ And again, when `Optional.Unsafe` is imported, it is possible to retrieve the va
402415
```csharp
403416
var value = option.ValueOrFailure();
404417
var anotherValue = option.ValueOrFailure("An error message");
418+
var potentiallyNullValue = option.ValueOrDefault();
405419
```
406420

407421
Values can be conveniently transformed using similar operations to that of the `Option<T>`. It is however important to note, that these transformations are all **short-circuiting**! That is, if an option is already none, the current exceptional value will remain, and not be replaced by any subsequent filtering. In this respect, this exceptional value is very similar to actual exceptions (hence the name).
@@ -454,7 +468,7 @@ Enumeration works identically to that of `Option<T>`:
454468
```csharp
455469
foreach (var value in option)
456470
{
457-
// Do something
471+
// Do something
458472
}
459473

460474
var enumerable = option.ToEnumerable();
@@ -505,3 +519,44 @@ var some = Option.Some<string, ErrorCode>("This is a string");
505519
var none = some.FlatMap(x => Option.None<string>(), ErrorCode.GeneralError);
506520
var none = some.FlatMap(x => Option.None<string>(), () => SlowOperation()); // Lazy variant
507521
```
522+
523+
### Working with collections
524+
525+
Optional provides a few convenience methods to ease interoperability with common .NET collections, and improve null safety a bit in the process.
526+
527+
LINQ provides a lot of useful methods when working with enumerables, but methods such as `FirstOrDefault`, `LastOrDefault`, `SingleOrDefault`, and `ElementAtOrDefault`, all return null (more precisely `default(T)`) to indicate that no value was found (e.g. if the enumerable was empty). Optional provides a safer alternative to all these methods, returning an option to indicate success/failure instead of nulls. As an added benefit, these methods work unambiguously for non-nullable/structs types as well, unlike their LINQ counterparts.
528+
529+
```csharp
530+
var option = values.FirstOrNone();
531+
var option = values.FirstOrNone(v => v != 0);
532+
var option = values.LastOrNone();
533+
var option = values.LastOrNone(v => v != 0);
534+
var option = values.SingleOrNone();
535+
var option = values.SingleOrNone(v => v != 0);
536+
var option = values.ElementAtOrNone(10);
537+
```
538+
539+
(Note that unlike `SingleOrDefault`, `SingleOrNone` never throws an exception but returns None in all "invalid" cases. This slight deviation in semantics was considered a safer alternative to the existing behavior, and is easy to work around in practice, if the finer granularity is needed.)
540+
541+
Optional provides a safe way to retrieve values from a dictionary:
542+
543+
```csharp
544+
var option = dictionary.GetValueOrNone("key");
545+
```
546+
547+
`GetValueOrNone` behaves similarly to `TryGetValue` on an `IDictionary<TKey, TValue>` or `IReadOnlyDictionary<TKey, TValue>`, but actually supports any `IEnumerable<KeyValuePair<TKey, TValue>>` (falling back to iteration, when a direct lookup is not possible).
548+
549+
Another common scenario, is to perform various transformations on an enumerable and ending up with a sequence of options (e.g. `IEnumerable<Option<T>>`). In many cases, only the non-empty options are relevant, and as such Optional provides a convenient method to flatten a sequence of options into a sequence containing all the inner values (whereas empty options are simply thrown away):
550+
551+
```csharp
552+
var options = new List<Option<int>> { Option.Some(1), Option.Some(2), Option.None<int>() };
553+
var values = option.Values(); // IEnumerable<int> { 1, 2 }
554+
```
555+
556+
When working with a sequence of `Option<T, TException>` a similar method is provided, as well a way to extract all the exceptional values:
557+
558+
```csharp
559+
var options = GetOptions(); // IEnumerable<Option<int, string>> { Some(1), None("error"), Some(2) }
560+
var values = options.Values(); // IEnumerable<int> { 1, 2 }
561+
var exceptions = options.Exceptions(); // IEnumerable<string> { "error" }
562+
```

build.cake

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ Task("Pack")
3131
.IsDependentOn("Test")
3232
.Does(() =>
3333
{
34-
Pack("Optional", new [] { "net35", "netstandard1.0" });
35-
Pack("Optional.Collections", new [] { "net35", "net45", "netstandard1.0" });
36-
Pack("Optional.Utilities", new [] { "net35", "net40", "netstandard1.0" });
34+
Pack("Optional", new [] { "net35", "net45", "netstandard1.0", "netstandard2.0" });
3735
});
3836

3937
RunTarget(target);

nuget/Optional.nuspec

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
<package >
33
<metadata>
44
<id>Optional</id>
5-
<version>3.3.0</version>
6-
<title>Optional</title>
5+
<version>4.0.0</version>
6+
<title>Optional: A robust option type</title>
77
<authors>Nils Lück</authors>
88
<owners>Nils Lück</owners>
99
<projectUrl>https://github.com/nlkl/Optional</projectUrl>
1010
<iconUrl>https://raw.githubusercontent.com/nlkl/Optional/master/icon/Icon.png</iconUrl>
1111
<requireLicenseAcceptance>false</requireLicenseAcceptance>
12-
<description>A robust option type.</description>
12+
<description>Optional is a robust option type for C#.</description>
1313
<copyright>Copyright 2015</copyright>
1414
<tags>Option Some None Maybe Either Exception Null Monad Monadic Functional</tags>
1515
</metadata>
68 KB
Binary file not shown.

src/Optional.Collections/DictionaryExtensions.cs

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)