Confession: kludgy lazy initializer


  • Considered Harmful

    So, throughout our medium-sized project we use the following, bog-standard lazy init pattern all over the place (C#):

    private Foo _myFoo;
    protected Foo MyFoo {
        get {
            if( _myFoo == null ) {
                _myFoo = GetMyFoo(); // usually inlined
            }
            return _myFoo;
        }
    }
    

    So, GetMyFoo() can be expensive and might legitimately return null, and I did not want to lose the benefit of the lazy initialization when the result is null. So I came up with the following kludge that abuses casting. Basically, the field is either null (not initialized), new object() (initialized but still null), or an instance of Foo.

    private object _myFoo;
    protected Foo MyFoo {
        get {
            if( _myFoo == null ) {
                _myFoo = GetMyFoo() ?? new object();
            }
            return _myFoo as Foo;
        }
    }
    

    This does exactly what I want it to do, but I'm wondering, is this code too retarded?



  • Yes, especially when it can be replaced with

    private Lazy<MyFoo> _myFoo = new Lazy<MyFoo>(() => GetMyFoo()); 

    protected Foo MyFoo { get { return _myFoo.Value; } };

      


  • Considered Harmful

    @Sutherlands said:

    Yes, especially when it can be replaced with

    private Lazy<MyFoo> _myFoo = new Lazy<MyFoo>(() => GetMyFoo()); 

    protected Foo MyFoo { get { return _myFoo.Value; } };

      

    This class is new to .NET 4.0, I believe. Also, I seem to remember not being able to access other data members or "this" in the lambda, which kind of sucks.

    Update: tested in VS2010, GetMyFoo has to be static; that makes it fairly worthless.



  • @joe.edwards said:

    This class is new to .NET 4.0, I believe. Also, I seem to remember not being able to access other data members or "this" in the lambda, which kind of sucks.

    Update: tested in VS2010, GetMyFoo has to be static; that makes it fairly worthless.

    No, it makes your C# skills fairly worthless.

    void Main()
    {
     Bar b = new Bar();
     b.MyFoo.Dump();
    }

    class Bar
    {
    private Lazy<Foo> _myFoo;
    public Bar()
     {
     _myFoo = new Lazy<Foo>(GetFoo);
     }
    private Foo GetFoo()
     {
     return new Foo();
     }
     public Foo MyFoo { get { return _myFoo.Value; } }
    }


    class Foo
    {
    }


  • Considered Harmful

    A slightly more accurate example:

    
    private Foo[] _enabledFoosForActiveRegion;
    protected IEnumerable<Foo> EnabledFoosForActiveRegion {
        get {
            if( _enabledFoosForActiveRegion == null ) {
                _enabledFoosForActiveRegion = AllFoos.Where( foo => foo.Region.Equals( ActiveRegion ) ).ToArray();
            }
            return _enabledFoosForActiveRegion;
        }
    }
    

    private Foo[] _selectedFoos;
    protected IEnumerable<Foo> SelectedFoos {
    get {
    if( _selectedFoos == null ) {
    _selectedFoos = EnabledFoosForActiveRegion.Where( foo => checkboxlist.SelectedValues.Contains( foo.ID ) ).ToArray();
    }
    return _selectedFoos;
    }
    }

    private int[] _selectedFooIds;
    protected IEnumerable<int> SelectedFooIds {
    get {
    if( _selectedFooIds== null ) {
    _selectedFooIds = SelectedFoos.Select( foo => foo.id ).ToArray();
    }
    return _selectedFooIds;
    }
    }

    There ends up being several of these daisy chained together, some of them actually look at the current request (ASP.NET), see how SelectedFoos is looking at the checked items in the list (unavailable in a static context).


  • Considered Harmful

    OK, so in your example we initialize everything in the constructor and have a 200 line constructor. I'm not seeing a compelling argument to upgrade the project to .NET 4.0 and rewrite a hundred working pages to use the new oh-shiny.

    Though I did remove the new object() kludge and just added a

    private bool _myFooInitialized;
    .



  • @joe.edwards said:

    OK, so in your example we initialize everything in the constructor and have a 200 line constructor.
    Considering you would only need 1 line in the constructor for each property, if you have 200 properties in your class you're doing it wrong.@joe.edwards said:
    I'm not seeing a compelling argument to upgrade the project to .NET 4.0

    It saves a lot of developer time without much upgrade cost?  That said, nobody said you should upgrade.  You failed to mention that you were using an old version of the framework in your OP.   My follow-up post was simply to address your fail at C#, where you can't even read a compile error correctly.@joe.edwards said:

    rewrite a hundred working pages
    You think you need to rewrite 100 working pages to fix this one function just because you upgraded the framework?  You can't just... fix this one function?  Glad you're not on my team.

     @joe.edwards said:

    Though I did remove the new object() kludge and just added a

    private _myFooInitialized;
    Maybe... if you made some sort of class... and added it to your project... that handled lazy initialization..... perhaps you could do this without upgrading the framework...

    Regarding the posted code, it looks like you're putting a bunch of business logic in the model.  If you really need the intermittent arrays multiple times then I guess it's the best way of doing it, but I'd seriously look at your architecture.


  • Considered Harmful

    @Sutherlands said:

    You think you need to rewrite 100 working pages to fix this one function just because you upgraded the framework?  You can't just... fix this one function?  Glad you're not on my team..


    It sounded like you were saying the initial pattern was bunk and should be ripped out and replaced with Lazy, for questionable benefit. Perhaps I misunderstood.
    The 200 line thing was assuming the lambdas were more than one line (4-6 on average), and was a bit hyperbolic.



  • @Sutherlands said:

    Maybe... if you made some sort of class... and added it to your project... that handled lazy initialization..... perhaps you could do this without upgrading the framework...

     Lets see... Open up a (possibly new) 4.0 project. Type in "Lazy<Object>" within a code block. Use resharper to go to the source code. Copy, Paste, tweak as appropriate....

     Nope, way too much work to possibly consider adding "some sort of class".....


  • Considered Harmful

    @TheCPUWizard said:

    Use resharper to go to the source code. Copy, Paste, tweak as appropriate....


    <3 Resharper


Log in to reply