Have you ever ever being implementing Choice<T>
practical kind? It’s mentioned right here:
https://app.pluralsight.com/library/programs/tactical-design-patterns-dot-net-control-flow/table-of-contents
Mainly it’s about utilizing IEnumerable<T>
with no or just one ingredient as an alternative of probably nullable object reference in C#. We will empower LINQ performance to streamline processing decreasing cyclomatic complexity due to no “if(null)” situations anymore.
My adoption of the concept seems to be this manner:
public struct Optionally available<T> : IEnumerable<T>
the place T : class
{
public static implicit operator Optionally available<T>(T worth)
{
return new Optionally available<T>(worth);
}
public static implicit operator T(Optionally available<T> elective)
{
return elective.Worth;
}
Optionally available(T worth)
: this()
{
Worth = worth;
}
public IEnumerator<T> GetEnumerator()
{
if (HasValue)
yield return Worth;
}
T Worth { get; }
bool HasValue => Worth != null;
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public override string ToString() => Worth?.ToString();
}
It’s not so unhealthy, we are able to use it as argument/return kind with computerized conversion to/from T when needed. Consumption instance would possibly seem like this:
var p = new Product("Milk");
var basket = ShoppingBasket.Empty
.Place(p)
.Place(p)
.Place(null);
It creates basket with single basket entry for 2 milk bins if the next is outlined:
public static class ShoppingBasket
{
public static readonly IEnumerable<BasketItem> Empty =
Enumerable.Empty<BasketItem>();
public static IEnumerable<BasketItem> Place(
this IEnumerable<BasketItem> basket,
Optionally available<Product> product) =>
basket
.SetOrAdd(
i => product.Comprises(i.Product),
i => i.OneMore(),
product.Choose(p => new BasketItem(p, 1)))
.ToArray();
}
The place:
public class BasketItem
{
public BasketItem(Product product, int amount)
{
Product = product;
Amount = amount;
}
public Product Product { get; }
public int Amount { get; }
public BasketItem OneMore() =>
new BasketItem(Product, Amount + 1);
}
public class Product
{
public Product(string identify)
{
Identify = identify;
}
public string Identify { get; }
}
I take advantage of this normal IEnumerable<T>
extension:
public static class EnumerableHelper
{
public static IEnumerable<T> SetOrAdd<T>(
this IEnumerable<T> supply,
Func<T, bool> predicate,
Func<T, T> set,
params T[] add)
{
return supply
.SetOrAdd(predicate, set, add as IEnumerable<T>);
}
public static IEnumerable<T> SetOrAdd<T>(
this IEnumerable<T> supply,
Func<T, bool> predicate,
Func<T, T> set,
IEnumerable<T> add)
{
var empty = Enumerable.Empty<T>();
foreach (var merchandise in supply)
if (predicate(merchandise))
{
yield return set(merchandise);
add = empty;
}
else
yield return merchandise;
foreach (var merchandise in add)
yield return merchandise;
}
}
Properly, it’s fairly practical so far as I can see. However the query is: does it make any sense? Cyclomatic complexity could be very low in ShoppingBasket class. However the identical time we nonetheless have three “digital paths” of execution to check – for “add” and “substitute” within the Place() technique + we additionally want to check for null product argument.
The issue is that they’re nearly invisible. What do you say? Would you personally favor to keep up such form of code in C#?