Click here to Skip to main content
Click here to Skip to main content

NonNullable Class Wrapper

By , 11 Apr 2008
 

Background

A class reference may be null, but in many cases a method that accepts a class reference as a parameter requires that the reference be non-null.

(For the example snippets, we'll assume that returning 0 or -1 when a null is passed is not desirable.)

The naive thing to do in such cases is to simply allow .NET to throw a NullReferenceException the first time it is dereferenced:

public int CountOccurrences ( string String1 , string String2 )
{
    // Count the number of times String2 occurs in String1 and return that value
}

But this doesn't tell the calling method which value is null.

A smarter technique is to test the passed references, and throw a more informative exception:

public int CountOccurrences ( string String1 , string String2 )
{
    if ( String1 == null )
    {
        throw new System.ArgumentNullException 
            ( "String1" , "You must provide a string to search" ) ;
    }

    if ( String2 == null )
    {
        throw new System.ArgumentNullException 
            ( "String2" , "You must provide a string to find" ) ;
    }

    // Count the number of times String2 occurs in String1 and return that value
}

But this performs the check on each parameter every time the method is called.
In most cases, this is fine, but in some cases this seems unnecessary. Consider a usage of this method that is used to count all the occurrences of some string in the lines of a text file. In such a case, after reading each line, that line and the string to find are passed to this method whereupon the references are checked for null. That seems reasonable, but the second parameter will be checked on each call even though it doesn't change.

The test for null doesn't impose much of a performance hit, so this isn't really a performance issue. I'm more concerned here with readability and maintainability; the second snippet above is safer than the first but is perhaps harder to read and maintain.

What if we could write the method as simply as the first example, but with the safety of the second? Maybe something like this:

public int CountOccurrences ( NonNullable<string> String1, NonNullable<string> String2 )
{
    // Count the number of times String2 occurs in String1 and return that value
}

Implementation of My NonNullable<T> Structure

Unlike classes, structs can't be null, so I'll use a struct to wrap the class reference. Furthermore, wrapping a struct in a NonNullable<T> would be needless. So the beginning of our struct looks like:

public struct NonNullable<T> where T : class
{
    // Members
}

The struct needs only one field; it'll hold the class reference. The value won't change, so we can make it readonly, therefore we can also make it public and avoid writing a property for it:

public readonly T Value ;

Only one constructor is required; it'll simply perform the check for null and throw or store:

public NonNullable
(
    T Value
)
{
    if ( Value == null )
    {
        throw ( new System.ArgumentNullException ( "Value" , "That value is null" ) ) ;
    }

    this.Value = Value ;

    return ;
}

On second thoughts, many people don't like constructors that throw exceptions, so let's write one that won't throw. This requires that we have a fall-back position that will still yield a valid instance. Something like this ought to do:

public NonNullable
(
    T              Value
    ,
    NonNullable<T> IfNull
)
{
    if ( Value == null )
    {
        this.Value = IfNull.Value ;
    }
    else
    {
        this.Value = Value ;
    }

    return ;
}

Every type should override ToString():

public override string
ToString
(
)
{
    return ( this.Value.ToString() ) ;
}

By adding an implicit conversion to NonNullable<T>, the calling method need not know what's going on:

public static implicit operator NonNullable<T>
(
    T Value
)
{
    return ( new NonNullable<T> ( Value ) ) ;
}

However, there is a performance hit involved. Worse than that, the guidelines for implicit conversions state that they shouldn't throw exceptions, and this one may. And, as with the naive non-checking method, the caller won't know which parameter was null.

Add an implicit conversion from NonNullable<T> for convenience:

public static implicit operator T
(
    NonNullable<T> Value
)
{
    return ( Value.Value ) ;
}

Lastly, a pair of Coalesce methods to wrap the first non-null reference among those provided:

public static NonNullable<T>
Coalesce
(
    params T[] Values
)
{
    return ( Coalesce ( (System.Collections.Generic.IEnumerable<T>) Values ) ) ;
}

public static NonNullable<T>
Coalesce
(
    System.Collections.Generic.IEnumerable<T> Values
)
{
    if ( Values == null )
    {
        throw ( new System.ArgumentNullException 
            ( "Values", "No values were provided" ) ) ;
    }

    foreach ( T t in Values )
    {
        if ( t != null )
        {
            return ( new NonNullable<T> ( t ) ) ;
        }
    }

    throw ( new System.ArgumentException 
        ( "No non-null values were provided" , "Values" ) ) ;
}

Using the Code

Methods can be written as in the third snippet above:

public int CountOccurrences 
    ( NonNullable<string> String1 , NonNullable<string> String2 )
{
    // Count the number of times String2 occurs in String1 and return that value
}

And because I included the implicit conversion, the calling method doesn't need to know what the called method is doing. In fact, the called method could be rewritten to use NonNullable parameters without affecting the caller.

A better practice, when calling a method that takes NonNullable parameters is to wrap the value separately from making the call, especially when a value is used many times without changing.

...

int count = 0 ;
string line ;

// Check this value only once!
NonNullable<string> safetext = new NonNullable<string> ( text ) ; 
NonNullable<string> safeline

while ( ( line = file.Read() ) != null )
{
    safeline = new NonNullable<string> ( line )
    count += CountOccurrences ( safeline , safetext ) ;
}

...

(Note to self: Test with in, out, and ref parameters.)

Performance

As stated above, this technique is not intended to improve performance; it is meant to reduce code and therefore maintenance. Having said that; my testing has shown a modest improvement in performance at best and a considerable performance hit at worst. After ten million calls to a method-that-takes-a-string:

x 10000000 00:00:00.1204537  // with no check
x 10000000 00:00:00.1416811  // check the parameter on each call
x 10000000 00:00:00.3597213  // implicit conversion to NonNullable<string> on each call
x 10000000 00:00:00.2382019  // direct call to the NonNullable<string> constructor 
                             // on each call
x 10000000 00:00:00.1083238  // call the NonNullable<string> constructor once

History

  • 2008-04-11: First written

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

PIEBALDconsult
Software Developer (Senior)
United States United States
Member
BSCS 1992 Wentworth Institute of Technology
 
Originally from the Boston (MA) area. Lived in SoCal for a while. Now in the Phoenix (AZ) area.
 
OpenVMS enthusiast, ISO 8601 evangelist, photographer, opinionated SOB
 
---------------
 
"If you need help knowing what to think, let me know and I'll tell you." -- Jeffrey Snover [MSFT]
 

"Typing is no substitute for thinking." -- R.W. Hamming
 
"I find it appalling that you can become a programmer with less training than it takes to become a plumber." -- Bjarne Stroustrup
 
ZagNut’s Law: Arrogance is inversely proportional to ability.
 
"Well blow me sideways with a plastic marionette. I've just learned something new - and if I could award you a 100 for that post I would. Way to go you keyboard lovegod you." -- Pete O'Hanlon
 
"linq'ish" sounds like "inept" in German -- Andreas Gieriet
 

"Things would be different if I ran the zoo." -- Dr. Seuss
 
"Wrong is evil, and it must be defeated." – Jeff Ello
 
"A good designer must rely on experience, on precise, logical thinking, and on pedantic exactness." -- Nigel Shaw
 

"Omit needless local variables." -- Strunk... had he taught programming
 
"DON'T BE LIBERAL IN WHAT YOU ACCEPT!"
 
"Software Engineers don't have Trophy Wives; they have Presentation Layers."
 
"We learn more from our mistakes than we do from getting it right the first time."
 
"I'm an old dog and I like old tricks."
 
"Sometimes the envelope pushes back and sometimes you get a really nasty paper cut."
 
"A method shall have one and only one return statement."
 
My first rule of debugging: "If you get a different error message, you're making progress."
 
My golden rule of database management: "Do not unto others' databases as you would not have done unto yours."
 
My general rule of software development: "Design should be top-down, but implementation should be bottom-up."
 
"Today's heresy is tomorrow's dogma."
or
"Today's dogma is yesterday's heresy."

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralNice Article about irritatable onememberthatraja15 Jan '10 - 11:23 
5 from me Blush | :O
GeneralRe: Nice Article about irritatable onemvpPIEBALDconsult16 Jan '10 - 4:42 
Thanks.
GeneralExcellentmember-Dy14 May '08 - 1:09 
Thank you posting this, it's aided my knowledge. The article is easy to follow and understand - a well deserved 5.
 
- Dy

GeneralRe: ExcellentmemberPIEBALDconsult14 May '08 - 13:01 
Glad to be of service.
GeneralNicemvpPete O'Hanlon13 Apr '08 - 9:46 
Even if I don't need to do this, it's nice to see you doing thing like this. A full blown 5 - fully inflated and brand spanky new.
 

Deja View - the feeling that you've seen this post before.
 

My blog | My articles



GeneralRe: NicememberPIEBALDconsult13 Apr '08 - 9:57 
Thanks.
GeneralUnfortunately, NonNullable<t>.Value can be null</t> [modified]memberDaniel Grunwald12 Apr '08 - 1:24 
And I don't think there's any way to prevent it.
NonNullable<string> n1 = default(NonNullable<string> ) ; // it's null!
NonNullable<string> n2 = new NonNullable<string>(); // also null!
(yes, this works even though you didn't declare this constructor, structs get an implicit constructor that initializes all fields to null/zero)
 
<div class="ForumMod">modified on Saturday, April 12, 2008 7:36 AM</div>
GeneralRe: Unfortunately, NonNullable.Value can be null [modified]memberPIEBALDconsult12 Apr '08 - 3:54 
D'oh!
 
Also if you just do
NonNullable<string> n1 ;
 

I guess I'll have to use property and throw when it's referenced.
 
Thanks.
 
modified on Saturday, April 12, 2008 10:13 AM

GeneralRe: Unfortunately, NonNullable.Value can be null [modified]memberPIEBALDconsult12 Apr '08 - 4:28 
Dang, dang dang, and double-dang!
 
Maybe I should just delete the thing.
 

 
Nah, I guess the caller would have to make an effort to do that so he deserves a NullReferenceException.
 
modified on Saturday, April 12, 2008 1:09 PM

AnswerRe: Unfortunately, NonNullable.Value can be nullmemberPete Appleton14 Apr '08 - 23:02 
How about making NonNullable a class with a private constructor?
 

public sealed class NonNullable where T:Class {
  private T _value;
 
  // No default ctor, thanks!
  private NonNullable() { throw new NotSupportedException("Please don't create via reflection"); }
   public NonNullable(T val) { if(val==null) throw ... }
 
  ...
}

 
That way, it shouldn't be possible to obtain a null NonNullable instance even by trying to use reflection to access the private ctor Big Grin | :-D
 
The wrinkle with this is seralization - if you want to make this serialisable for some reason then you'll HAVE to implement ISerializable rather than merely decorating with the SerializableAttribute. If you don't then it'll try to use the default ctor and duly blow!
 
Great article, btw - got my 5!
 
--
What's a signature?

GeneralRe: Unfortunately, NonNullable.Value can be nullmemberMember 43198654 May '08 - 13:59 
I've overcome this by inheriting from new(), and changing the public readonly Value, to a getter/setter, where the getter will return a new T(), if fValue is null.
 
public struct NonNullable<T> where T : class, new()
{
...
/// <summary>The wrapped value.</summary>
public T Value
{
get { return fValue ?? new T(); }
private set { fValue = value; }
}
T fValue;
}


It does mean that the given class MUST have a parameterless constructor though. Frown | :(
 
I've also removed the need to pass a default value, again putting the onus on the calling method to ensure the passed value is not null. This is easily done by documenting the appropriate resultant syntax eg: NonNullable() b = class ?? (new class());
 
And one should also test for equality:
public override bool Equals(object obj)
{
if (!(obj is NonNullable<T> ))
{
return false;
}
 
return Equals((NonNullable<T> )obj);
}
 
public bool Equals(NonNullable<T> other)
{
return Value.Equals(other.Value);
}
 
public static bool operator ==(NonNullable<T> t1, NonNullable<T> t2)
{
return t1.Equals(t2);
}
 
public static bool operator !=(NonNullable<T> t1, NonNullable<T> t2)
{
return !t1.Equals(t2);
}
 
public override int GetHashCode()
{
return Value.GetHashCode();
}

 

I hope this helps...
GeneralRe: Unfortunately, NonNullable.Value can be nullmemberDean Goddard4 May '08 - 14:06 
I just want to add, that my architectural team think this struct is great and welcomed my addition to it.
 
And thank you.
GeneralRe: Unfortunately, NonNullable.Value can be nullmemberPIEBALDconsult4 May '08 - 16:51 
Well, I prefer to get an Exception rather than have it go merrily along.
 
Plus, your method will instantiate a new T each time Value is called, better to do it once in the constructor.
GeneralInteresting!protectorMarc Clifton11 Apr '08 - 11:41 
A very readable article, and you put a lot of thought into this. The assumption that the instance being passed in can be reused to avoid the constructor hit makes this somewhat special purpose. Also, I've gone the "use a default if the parameter is null" route before and it's gotten me in more trouble than not, so I don't mind if constructors throw exceptions, but actually what I try to discipline myself to do (because it makes declarative systems happier as well) is to have parameterless constructors and an "Initialize" method.
 
Marc
 

GeneralRe: Interesting!memberPIEBALDconsult11 Apr '08 - 12:00 
Thanks.
 
Marc Clifton wrote:
The assumption that the instance being passed in can be reused

 
Where do you see that?
 

Marc Clifton wrote:
have parameterless constructors and an "Initialize" method.

 
I'm in the "once an instance is created it should be usable" camp.
GeneralRe: Interesting!protectorMarc Clifton11 Apr '08 - 12:21 
PIEBALDconsult wrote:
Where do you see that?

 
Well, it pops out in your performance metrics.
 
Marc
 

GeneralRe: Interesting!memberPIEBALDconsult11 Apr '08 - 13:27 
" the instance being passed in can be reused "
 
Ah, I think I misunderstood then, I had read it as "being able to put a different value into an existing wrapper" which I disallow by the use of readonly.
GeneralRe: Interesting!memberPIEBALDconsult11 Apr '08 - 14:58 
And perhaps I wasn't clear; the code contains both constructors.
GeneralRe: Interesting!memberaspdotnetdev10 Sep '10 - 6:53 
Marc Clifton wrote:
what I try to discipline myself to do (because it makes declarative systems happier as well) is to have parameterless constructors and an "Initialize" method

 
I recommend creating a private constructor with a public static method on the class that is able to create the instance. That way, any exception throwing logic can reside outside of the constructor while still having control over creation of the object.

GeneralRe: Interesting!mvpPIEBALDconsult11 Sep '10 - 15:45 
That's good in many situations, as needed, but doesn't work real well in many situations -- plugins for instance -- so should not be used as SOP.
GeneralI wrote exactly the opposite code last nite!member leppie 11 Apr '08 - 11:22 
I was writing a typesafe pointer/box for value types Smile | :)
 
<font color="Blue">sealed</font> <font color="Blue">class</font> <font color="Teal">Box</font><font color="DarkBlue"><</font><font color="Teal">T</font><font color="DarkBlue">></font> <font color="Blue">where</font> <font color="Teal">T</font> <font color="DarkBlue">:</font> <font color="Blue">struct</font>
<font color="DarkBlue">{</font>
  <font color="Blue">private</font> <font color="Teal">Box</font><font color="DarkBlue">(</font><font color="Teal">T</font> t<font color="DarkBlue">)</font>
  <font color="DarkBlue">{</font>
    value <font color="DarkBlue">=</font> t<font color="DarkBlue">;</font>
  <font color="DarkBlue">}</font>
 
  <font color="Blue">readonly</font> <font color="Teal">T</font> value<font color="DarkBlue">;</font>
 
  <font color="Blue">public</font> <font color="Blue">static</font> <font color="Blue">implicit</font> <font color="Blue">operator</font> <font color="Teal">T</font><font color="DarkBlue">(</font><font color="Teal">Box</font><font color="DarkBlue"><</font><font color="Teal">T</font><font color="DarkBlue">></font> b<font color="DarkBlue">)</font>
  <font color="DarkBlue">{</font>
    <font color="Blue">return</font> b<font color="DarkBlue">.</font>value<font color="DarkBlue">;</font>
  <font color="DarkBlue">}</font>
 
  <font color="Blue">public</font> <font color="Blue">static</font> <font color="Blue">implicit</font> <font color="Blue">operator</font> <font color="Teal">Box</font><font color="DarkBlue"><</font><font color="Teal">T</font><font color="DarkBlue">></font><font color="DarkBlue">(</font><font color="Teal">T</font> t<font color="DarkBlue">)</font>
  <font color="DarkBlue">{</font>
    <font color="Blue">return</font> <font color="Blue">new</font> <font color="Teal">Box</font><font color="DarkBlue"><</font><font color="Teal">T</font><font color="DarkBlue">></font><font color="DarkBlue">(</font>t<font color="DarkBlue">)</font><font color="DarkBlue">;</font>
  <font color="DarkBlue">}</font>
 
  <font color="Blue">public</font> <font color="Blue">override</font> <font color="Blue">bool</font> Equals<font color="DarkBlue">(</font><font color="Blue">object</font> obj<font color="DarkBlue">)</font>
  <font color="DarkBlue">{</font>
    <font color="Blue">return</font> value<font color="DarkBlue">.</font>Equals<font color="DarkBlue">(</font>obj<font color="DarkBlue">)</font><font color="DarkBlue">;</font>
  <font color="DarkBlue">}</font>
 
  <font color="Blue">public</font> <font color="Blue">override</font> <font color="Blue">int</font> GetHashCode<font color="DarkBlue">(</font><font color="DarkBlue">)</font>
  <font color="DarkBlue">{</font>
    <font color="Blue">return</font> value<font color="DarkBlue">.</font>GetHashCode<font color="DarkBlue">(</font><font color="DarkBlue">)</font><font color="DarkBlue">;</font>
  <font color="DarkBlue">}</font>
 
  <font color="Blue">public</font> <font color="Blue">override</font> <font color="Blue">string</font> ToString<font color="DarkBlue">(</font><font color="DarkBlue">)</font>
  <font color="DarkBlue">{</font>
    <font color="Blue">return</font> value<font color="DarkBlue">.</font>ToString<font color="DarkBlue">(</font><font color="DarkBlue">)</font><font color="DarkBlue">;</font>
  <font color="DarkBlue">}</font>
<font color="DarkBlue">}</font>
Laugh | :laugh:
 
xacc.ide - now with TabsToSpaces support
IronScheme - 1.0 alpha 3 out now

GeneralRe: I wrote exactly the opposite code last nite!memberPIEBALDconsult11 Apr '08 - 11:37 
Well, I think pointers should go in boxes, at least on Fridays. Big Grin | :-D
 

This is the outcome of the General Discussion I began a week ago
 
http://www.codeproject.com/script/Forums/View.aspx?fid=1642&tid=2492630[^]
GeneralRe: I wrote exactly the opposite code last nite!member leppie 11 Apr '08 - 11:55 
I see Smile | :)
 
Mine is to see what the overhead of mine would be compared to boxing/unboxing, with respect to performance and the GC. I am yet to do tests...
 
xacc.ide - now with TabsToSpaces support
IronScheme - 1.0 alpha 3 out now

GeneralRe: I wrote exactly the opposite code last nite!memberPIEBALDconsult11 Apr '08 - 12:01 
Oh, I also forgot to say, "thanks".

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 11 Apr 2008
Article Copyright 2008 by PIEBALDconsult
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid