Methods for Lazy Initialization with properties
Asked Answered
C

3

8

I'm currently altering a widely used class to move as much of the expensive initialization from the class constructor into Lazy Initialized properties. Below is an example (in c#):

Before:

public class ClassA
{
    public readonly ClassB B;

    public void ClassA()
    {
        B = new ClassB();
    }
}

After:

public class ClassA
{
    private ClassB _b;

    public ClassB B
    {
        get
        {
            if (_b == null)
            {
                _b = new ClassB();
            }

            return _b;
        }
    }
}

There are a fair few more of these properties in the class I'm altering, and some are not used in certain contexts (hence the Laziness), but if they are used they're likely to be called repeatedly.

Unfortunately, the properties are often also used inside the class. This means there is a potential for the private variable (_b) to be used directly by a method without it being initialized.

Is there a way to make only the public property (B) available inside the class, or even an alternative method with the same initialized-when-needed?

This is reposted from Programmers (not subjective enough apparently): https://softwareengineering.stackexchange.com/questions/34270/best-methods-for-lazy-initialization-with-properties

Consummate answered 6/1, 2011 at 15:57 Comment(0)
C
6

You could consider pushing the lazy properties into a base class to avoid direct access to the backing variable. Not ideal I know. I've always thought this was something lacking in C# i.e. direct support for lazy properties.

Contuse answered 6/1, 2011 at 16:0 Comment(7)
It would be nice if you could attribute a property to make it lazy to remove all this need and have implicit lazy loaded properties without needing to refer to .Value.Spiraea
@Spiraea When I first saw automatic properties I thought that they had added lazy capability - unfortunately not. It would be great to be able to define an automatic property with an initializer func.Contuse
@chibacity interesting thought, I wonder if that would be achievable... time to start playing ;)Spiraea
@Spiraea They would certainly have to introduce some new syntax fluff and compiler backend. I think the hard part would be to achieve something that was readable and easily understandable. You may also have to bring in threading configuration unless you went thread-safe by default. Getting some nice clear syntax would be tricky. public Bling B { lazyget : new Bling(); } :)Contuse
@chibacity Could the new Bling() not be inferred by the return type and the fact that Bling has a parameterless constructor? Thus you'd just need public Bling B { lazyget; } of course, if you don't have a parameterless constructor, it would need a syntax similar to yours.Spiraea
@Spiraea For a default constructor could be inferred nicely, although I would be concerned how users might interpret compile errors if the default constructor was removed. You need a good place to out the red squiggle :) I quite like the idea of having the func though as this would fit in nicely with DI.Contuse
@mquander, @BenAlabaster: Your answers are very interesting, but I've chosen this one as the use of a straightforward base class is a solution I can most readily use.Consummate
D
16

Well, my recommended solution would be to tell your coworker to use the property, not the field. But you could idiot-proof it to some degree like this:

public class ClassA
{
    private Lazy<ClassB> _b = new Lazy<ClassB>(() => new ClassB());

    public ClassB B
    {
        get
        {
            return _b.Value;
        }
    }
}

Now it's pretty hard to screw up.

Distaff answered 6/1, 2011 at 15:59 Comment(18)
What exactly is Lazy<ClassB> in this example? Thanks.Commercialism
Nice, I wasn't aware of the Lazy<T> class; however it appears new in 4, not sure what the OP is using.Bullate
+1 Much nicer than my suggestion :) Any performance implications for high access properties?Contuse
Lazy<ClassB> encapsulates the "initialize on first use" functionality. The first time you call .Value, it will run the function you pass in to generate a ClassB instance, and cache it. In the future, when you call .Value, it will return the one it created the first time.Distaff
@Aaron: 3.0 in this project, unfortunately.Consummate
Since his first example was not threadsafe, I assume he's not concerned about thread-safety. And I don't know what the performance implications are, except that they must be so minor that I would only worry about it in some absurdly important inner loop.Distaff
@Stuart: It's so trivial to write a Lazy<> class that you might want to consider just doing it yourself if you're writing this pattern often.Distaff
Didn't know about this. Pure awesome.Zeller
@mquander: Certainly worth considering.Consummate
@mquander - I am not sure what you mean by not Threadsafe - which part isnt?Cyclic
Reference for implementations of Lazy<T> for 3.5: stackoverflow.com/questions/3462675 and stackoverflow.com/questions/3207580Kovno
@mquander One step further and you could idiot proof it completely by pushing the lazy instantiation up to a base class and have the property defer to the base property. Then the coworker wouldn't need to be told not to use the field, he wouldn't have a choice but to use the property and it has the double effect of removing extraneous fields from your ClassA instance.Spiraea
@BenAlabaster: Sure, you could do that, but my point was that it's very hard to mis-use it as it stands. The only way to try to get a ClassB out of the field is to call .Value, and if the coworker does that (because he doesn't know about the property) then things are just fine anyway. Personally, I wouldn't find it worth the effort and lines of code to bother beyond that.Distaff
@Ryan: The part that isn't threadsafe by default is: what happens when two threads simultaneously call it for the "first time" and race to initialize it? Check MSDN for the LazyThreadSafetyMode enum that Hans mentioned. It addresses that problem.Distaff
@mquander I guess it depends on your definition of code clarity. For me, having less code in my working class - i.e. ClassA would win out over the few extra lines of code in the base class that would be ignored for the most part. I'm not saying one way or the other is right, just offering an alternative point of view.Spiraea
@mquander - I thought you were referring to the OP's code, not the Lazy class. There are no static vars or other shared resources in the OP's code so how can it not be thread safe? If the calling class uses static ClassA = then its up to them to ensure thread safety, no?Cyclic
@Ryan: Not sure what you mean by "up to them" -- given the OP's original code, if two threads have a reference to the same ClassA object called A, and both of them call A.B at about the same time, they may both initialize and return different ClassB objects. That's what I mean by the OP's code being not threadsafe. You would need, e.g., a lock around the null check and initialization in order to prevent this.Distaff
Ohhhkay - gotcha, my brain f@rt! Or you put the standard disclaimer "Any public static members of this type are thread safe. Any instance members are not guaranteed to be thread safe." ;)Cyclic
C
6

You could consider pushing the lazy properties into a base class to avoid direct access to the backing variable. Not ideal I know. I've always thought this was something lacking in C# i.e. direct support for lazy properties.

Contuse answered 6/1, 2011 at 16:0 Comment(7)
It would be nice if you could attribute a property to make it lazy to remove all this need and have implicit lazy loaded properties without needing to refer to .Value.Spiraea
@Spiraea When I first saw automatic properties I thought that they had added lazy capability - unfortunately not. It would be great to be able to define an automatic property with an initializer func.Contuse
@chibacity interesting thought, I wonder if that would be achievable... time to start playing ;)Spiraea
@Spiraea They would certainly have to introduce some new syntax fluff and compiler backend. I think the hard part would be to achieve something that was readable and easily understandable. You may also have to bring in threading configuration unless you went thread-safe by default. Getting some nice clear syntax would be tricky. public Bling B { lazyget : new Bling(); } :)Contuse
@chibacity Could the new Bling() not be inferred by the return type and the fact that Bling has a parameterless constructor? Thus you'd just need public Bling B { lazyget; } of course, if you don't have a parameterless constructor, it would need a syntax similar to yours.Spiraea
@Spiraea For a default constructor could be inferred nicely, although I would be concerned how users might interpret compile errors if the default constructor was removed. You need a good place to out the red squiggle :) I quite like the idea of having the func though as this would fit in nicely with DI.Contuse
@mquander, @BenAlabaster: Your answers are very interesting, but I've chosen this one as the use of a straightforward base class is a solution I can most readily use.Consummate
S
3

@chibacity posted (and subsequently) deleted [and later undeleted :P] an alternative option using an abstract base class. While it may not be ideal in terms of code distribution it does provide a nice encapsulation removing a lot of code clutter making for a cleaner and more succinct ClassA. For instance, you could consider combining the techniques to achieve both goals:

public class ClassB { /* Class to be lazily instantiated */ }

public abstract class BaseA
{
    private Lazy<ClassB> _b = new Lazy<ClassB>(() => new ClassB());
    public virtual ClassB B { get { return _b.Value; } }
}

public class ClassA : BaseA
{
    public override ClassB B { get { return base.B; } }
}

At first glance, it seems like this is more long winded, but when you consider that ClassA which is the class you would be working in and with, this now means that all your references are going through the same property - there is no extraneous unnecessary field causing potential confusion, there's no bypassing the property to reference _b directly and there's no need to tell your coworker which to use... there's only one.

Not saying this is the right way to do this or that this is a pattern that should or shouldn't be followed, I'm just pointing out the advantages of what @chibacity suggested that may otherwise go unnoticed.

It would be nice if you could have implicit lazy loaded properties without having to refer to B.Value... for instance:

[Lazy]
public ClassB B { get; }

or for objects without parameterless constructors

[Lazy(() => new ClassB("Hello", "World"))]
public ClassB B { get; }

or perhaps as @chibacity suggested in a comment

public ClassB B { lazyget; }

or

public ClassB B { lazyget : new ClassB(); }

Alas, I don't think any of these are currently available solutions in any form...

Spiraea answered 6/1, 2011 at 16:55 Comment(2)
I am liking the syntax with the func and the attribute, but unfortunately attribute arguments have to be compile-time constant, so that's out - looks v. nice though. :)Contuse
@chibacity That's one thing I'm always conscious of when I'm writing APIs - how do I want the syntax to look when I'm calling this or using this feature? Usually I design what I want my calls to look like before I design my API so that everything is just so.Spiraea

© 2022 - 2024 — McMap. All rights reserved.