Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible pitfalls of using this (extension method based) shorthand

C#6 Update

In C#6 ?. is now a language feature:

// C#1-5
propertyValue1 = myObject != null ? myObject.StringProperty : null; 

// C#6
propertyValue1 = myObject?.StringProperty;

The question below still applies to older versions, but if developing a new application using the new ?. operator is far better practice.

Original Question:

I regularly want to access properties on possibly null objects:

string propertyValue1 = null;
if( myObject1 != null )
    propertyValue1 = myObject1.StringProperty;

int propertyValue2 = 0;
if( myObject2 != null )
    propertyValue2 = myObject2.IntProperty;

And so on...

I use this so often that I have a snippet for it.

You can shorten this to some extent with an inline if:

propertyValue1 = myObject != null ? myObject.StringProperty : null;

However this is a little clunky, especially if setting lots of properties or if more than one level can be null, for instance:

propertyValue1 = myObject != null ? 
    (myObject.ObjectProp != null ? myObject.ObjectProp.StringProperty) : null : null;

What I really want is ?? style syntax, which works great for directly null types:

int? i = SomeFunctionWhichMightReturnNull();
propertyValue2 = i ?? 0;

So I came up with the following:

public static TResult IfNotNull<T, TResult>( this T input, Func<T, TResult> action, TResult valueIfNull )
    where T : class
{
    if ( input != null ) return action( input );
    else return valueIfNull;
}

//lets us have a null default if the type is nullable
public static TResult IfNotNull<T, TResult>( this T input, Func<T, TResult> action )
    where T : class
    where TResult : class
{ return input.IfNotNull( action, null ); }

This lets me us this syntax:

propertyValue1 = myObject1.IfNotNull( x => x.StringProperty );
propertyValue2 = myObject2.IfNotNull( x => x.IntProperty, 0);

//or one with multiple levels
propertyValue1 = myObject.IfNotNull( 
    o => o.ObjectProp.IfNotNull( p => p.StringProperty ) );

This simplifies these calls, but I'm not sure about checking this sort of extension method in - it does make the code a little easier to read, but at the cost of extending object. This would appear on everything, although I could put it in a specifically referenced namespace.

This example is a rather simple one, a slightly more complex one would be comparing two nullable object properties:

if( ( obj1 == null && obj2 == null ) || 
    ( obj1 != null && obj2 != null && obj1.Property == obj2.Property ) )
    ...

//becomes
if( obj1.NullCompare( obj2, (x,y) => x.Property == y.Property ) 
    ...

What are the pitfalls of using extensions in this way? Are other coders likely to be confused? Is this just abuse of extensions?


I guess what I really want here is a compiler/language extension:

propertyValue1 = myObject != null ? myObject.StringProperty : null;

//becomes
propertyValue1 = myObject?StringProperty;

This would make the complex case far easier:

propertyValue1 = myObject != null ? 
    (myObject.ObjectProp != null ? myObject.ObjectProp.StringProperty) : null

//becomes
propertyValue1 = myObject?ObjectProp?StringProperty;

This would only work for value types, but you could return nullable equivalents:

int? propertyValue2 = myObject?ObjectProp?IntProperty;

//or

int propertyValue3 = myObject?ObjectProp?IntProperty ?? 0;
like image 629
Keith Avatar asked Sep 23 '08 19:09

Keith


1 Answers

We independently came up with the exact same extension method name and implementation: Null-propagating extension method. So we don't think it's confusing or an abuse of extension methods.

I would write your "multiple levels" example with chaining as follows:

propertyValue1 = myObject.IfNotNull(o => o.ObjectProp).IfNotNull(p => p.StringProperty);

There's a now-closed bug on Microsoft Connect that proposed "?." as a new C# operator that would perform this null propagation. Mads Torgersen (from the C# language team) briefly explained why they won't implement it.

like image 166
Bradley Grainger Avatar answered Sep 23 '22 12:09

Bradley Grainger