In my current project for the year of active development there was not a single NullReferenceException. I can not without reason to believe that this is a consequence of the use of the techniques described below.
public interface IUserRepo { User Get(int id); User Find(int id); }
public interface IUserRepo { User Get(int id); Maybe<User> Find(int id); }
Immediately make a reservation that the first method can still return null. There is no simple way to prohibit it at the language level. However, you can do this at least at the agreement level in the development team. The success of such an undertaking depends on the people; In my project, such an agreement is accepted and successfully respected.
Of course, you can go ahead and embed in the build process of checking for the presence of the null keyword in the source code (with the specified exceptions to this rule). But in this there was no need, just enough internal discipline.
In general, you can go even further, for example, forcibly inject into all appropriate Contract.Ensure methods (Contract.Result <T> ()! = Null) through some AOP solution, for example PostSharp, in this case even team members with low discipline will not be able to return the ill-fated null.
// null var user = repo.Find(userId); // User, Maybe<User> var userName = user.Name; // , Maybe Name var maybeUser = repo.Find(userId); // , string userName; if (maybeUser.HasValue) // { var user = maybeUser.Value; userName = user.Name; } else userName = "unknown";
string userName = repo.Find(userId).Select(u => u.Name).OrElse("unknown");
string userName = (from user in repo.Find(userId) select user.Name).OrElse("unknown");
( from roleAProfile in provider.FindProfile(userId, type: "A") from roleBProfile in provider.FindProfile(userId, type: "B") from roleCProfile in provider.FindProfile(userId, type: "C") where roleAProfile.IsActive() && roleCProfile.IsPremium() let user = repo.GetUser(userId) select user ).Do(HonorAsActiveUser);
var maybeProfileA = provider.FindProfile(userId, type: "A"); if (maybeProfileA.HasValue) { var profileA = maybeProfileA.Value; var maybeProfileB = provider.FindProfile(userId, type: "B"); if (maybeProfileB.HasValue) { var profileB = maybeProfileB.Value; var maybeProfileC = provider.FindProfile(userId, type: "C"); if (maybeProfileC.HasValue) { var profileC = maybeProfileC.Value; if (profileA.IsActive() && profileC.IsPremium()) { var user = repo.GetUser(userId); HonorAsActiveUser(user); } } } }
var admin = users.MaybeFirst(u => u.IsAdmin); // FirstOrDefault(u => u.IsAdmin); Console.WriteLine("Admin is {0}", admin.Select(a => a.Name).OrElse("not found"));
Package name Nuget and type type | Hasvalue | Value | Fluentapi | LINQ support | IEnumerable integration | Notes and source code |
---|---|---|---|---|---|---|
Option, class | there is | no, only pattern-matching | minimal | not | not | github.com/tejacques/Option |
Strilanc.Value.May, struct | there is | no, only pattern-matching | rich | there is | there is | Accepts null as a valid value in May. github.com/Strilanc/May |
Options, struct | there is | there is | the average | there is | there is | Also available as Either. github.com/davidsidlinger/options |
Nevernull class | there is | there is | the average | not | not | github.com/Bomret/NeverNull |
Functional.Maybe, struct | there is | there is | rich | there is | there is | github.com/AndreyTsvetkov/Functional.Maybe |
Maybe no type | - | - | minimal | not | - | extension methods work with the usual null github.com/hazzik/Maybe upd: and here is a screencast from mezastel with a similar approach and a detailed explanation: www.techdays.ru/videos/4448.html |
WeeGems.Options, struct | there is | there is | minimal | not | not | There are also other functional utilities: memoization, partial application of functions. bitbucket.org/MattDavey/weegems |
Source: https://habr.com/ru/post/200100/
All Articles