Is there a convenient syntax for returning null, and not exceptions when accessing a property of a null object

Consider this simple C # example:

var person = new Person {Name = "Fred", MailingAddress=null }; var result = String.Format("{0} lives at {1}",person.Name, person.MailingAddress.Street); 

it is clear that this will throw a NullReferenceException because skipping MailingAddress is null.

I could rewrite the second line as:

 var result = String.Format("{0} lives at {1}", person.Name, person.MailingAddress == null ? (String)null : person.MailingAddress.Street); 

Is there an easier way to express this?

+4
source share
4 answers

There is no good syntax for this. The coalesce operator is part of it, but you need to handle the traversal through zero, and not just replace the zero. One thing you could do is to have a static "null object" for the class, for example:

 public class Address { public static Address Null = new Address(); // Rest of the class goes here } 

Then you can use the coalesce operator as follows:

 (person.MailingAddress ?? Address.Null).Street 

If you want to go the route of the extension method, you can do something like this:

 public static class NullExtension { public static T OrNew<T>(this T thing) where T: class, new() { return thing ?? new T(); } } 

Then you could do:

 (person.MailingAddress.OrNew().Street) 
+3
source

This code is technically a violation of the law of the Law of Demeter , so some of them believe that it is a bad form to write this in the first place.

No, there is no native syntax for doing what you want, but moving this code to a property in the Person class will make this call code cleaner and also bring you into compliance with the demeter law.

 public string StreetAddress{ get { return this.MailingAddress == null ? (String)null : person.MailingAddress.Street; } } 
+4
source

You can use a device based on expression trees, so you write

 var n = person.NullPropagate(p => p.Contact.MailingAddress.StreetAddress.Number); /* having the effect of: (person == null) ? defaultValue : (person.Contact == null) ? defaultValue : (person.Contact.MailingAddress == null) ? defaultValue : (person.Contact.MailingAddress.StreetAddress == null) ? defaultValue : person.Contact.MailingAddress.StreetAddress.Number; */ 

Disclaimer I did not write this code, I just do not know where I found it from either. Does anyone recognize this helper?

 public static R NullPropagate<T, R>(this T source, Expression<Func<T, R>> expression, R defaultValue) { var safeExp = Expression.Lambda<Func<T, R>>( WrapNullSafe(expression.Body, Expression.Constant(defaultValue)), expression.Parameters[0]); var safeDelegate = safeExp.Compile(); return safeDelegate(source); } private static Expression WrapNullSafe(Expression expr, Expression defaultValue) { Expression obj; Expression safe = expr; while (!IsNullSafe(expr, out obj)) { var isNull = Expression.Equal(obj, Expression.Constant(null)); safe = Expression.Condition (isNull, defaultValue, safe); expr = obj; } return safe; } private static bool IsNullSafe(Expression expr, out Expression nullableObject) { nullableObject = null; if (expr is MemberExpression || expr is MethodCallExpression) { Expression obj; MemberExpression memberExpr = expr as MemberExpression; MethodCallExpression callExpr = expr as MethodCallExpression; if (memberExpr != null) { // Static fields don't require an instance FieldInfo field = memberExpr.Member as FieldInfo; if (field != null && field.IsStatic) return true; // Static properties don't require an instance PropertyInfo property = memberExpr.Member as PropertyInfo; if (property != null) { MethodInfo getter = property.GetGetMethod(); if (getter != null && getter.IsStatic) return true; } obj = memberExpr.Expression; } else { // Static methods don't require an instance if (callExpr.Method.IsStatic) return true; obj = callExpr.Object; } // Value types can't be null if (obj.Type.IsValueType) return true; // Instance member access or instance method call is not safe nullableObject = obj; return false; } return true; } 
+1
source

You can use this extension method:

 public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator, TResult failureValue) where TInput : class { return (value != null) ? evaluator(value) : failureValue; } 

Example:

 person.MailingAddress.MayBe(p=>p.Street,default(Street)) 
0
source

Source: https://habr.com/ru/post/1383091/


All Articles