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

Community
  • 1
  • 1
Stu Pegg
  • 1,287
  • 2
  • 14
  • 41

3 Answers3

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.

mqp
  • 70,359
  • 14
  • 95
  • 123
  • What exactly is `Lazy` in this example? Thanks. – Hamish Grubijan Jan 06 '11 at 16:02
  • Nice, I wasn't aware of the Lazy class; however it appears new in 4, not sure what the OP is using. – Aaron McIver Jan 06 '11 at 16:02
  • +1 Much nicer than my suggestion :) Any performance implications for high access properties? – Tim Lloyd Jan 06 '11 at 16:03
  • `Lazy` 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. – mqp Jan 06 '11 at 16:05
  • @Aaron: 3.0 in this project, unfortunately. – Stu Pegg Jan 06 '11 at 16:05
  • 1
    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. – mqp Jan 06 '11 at 16:06
  • 1
    @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. – mqp Jan 06 '11 at 16:07
  • Didn't know about this. Pure awesome. – annakata Jan 06 '11 at 16:10
  • @mquander: Certainly worth considering. – Stu Pegg Jan 06 '11 at 16:10
  • @mquander - I am not sure what you mean by not Threadsafe - which part isnt? – Ryan Jan 06 '11 at 16:22
  • Reference for implementations of Lazy for 3.5: http://stackoverflow.com/questions/3462675 and http://stackoverflow.com/questions/3207580 – Kramii Jan 06 '11 at 16:26
  • 1
    @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. – BenAlabaster Jan 06 '11 at 17:02
  • 1
    @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. – mqp Jan 06 '11 at 17:50
  • 1
    @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. – mqp Jan 06 '11 at 17:51
  • @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. – BenAlabaster Jan 06 '11 at 18:04
  • @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? – Ryan Jan 06 '11 at 18:38
  • @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. – mqp Jan 06 '11 at 19:08
  • 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." ;) – Ryan Jan 06 '11 at 19:28
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.

Tim Lloyd
  • 37,954
  • 10
  • 100
  • 130
  • 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. – BenAlabaster Jan 06 '11 at 17:12
  • @BenAlabaster 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. – Tim Lloyd Jan 06 '11 at 17:15
  • @chibacity interesting thought, I wonder if that would be achievable... time to start playing ;) – BenAlabaster Jan 06 '11 at 17:19
  • @BenAlabaster 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(); }` :) – Tim Lloyd Jan 06 '11 at 17:28
  • @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. – BenAlabaster Jan 06 '11 at 17:33
  • @BenAlabaster 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. – Tim Lloyd Jan 06 '11 at 17:37
  • @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. – Stu Pegg Jan 10 '11 at 10:08
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...

BenAlabaster
  • 39,070
  • 21
  • 110
  • 151
  • 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. :) – Tim Lloyd Jan 06 '11 at 18:48
  • @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. – BenAlabaster Jan 06 '11 at 18:59