default.kramer
default.kramer

Reputation: 6103

Lazy property requiring "this"

Here is an example of a property I have, coded as simply as possible

private IEnumerable<int> _blocks;
private bool _blocksEvaluated;
public IEnumerable<int> Blocks
{
    get
    {
        if (!_blocksEvaluated)
        {
            _blocksEvaluated = true;
            _blocks = this.CalculateBlocks(0).FirstOrDefault();
        }
        return _blocks;
    }
}

This is verbose; I would like to make it more concise if possible. The following would be acceptable...

private Lazy<IEnumerable<int>> _blocks = 
    new Lazy<IEnumerable<int>>(() => this.CalculateBlocks(0).FirstOrDefault());

... but it doesn't compile.

Keyword 'this' is not valid in a static property, static method, or static field initializer

So I came up with the following

struct MyLazy<TResult>
{
    private bool evaluated;
    private TResult result;

    public TResult Evaluate(Func<TResult> func)
    {
        if (!evaluated)
        {
            evaluated = true;
            result = func();
        }
        return result;
    }
}

private MyLazy<IEnumerable<int>> _blocks;
public IEnumerable<int> Blocks
{
    get { return _blocks.Evaluate(() => this.CalculateBlocks(0).FirstOrDefault()); }
}

Which I like best, but is there a better way?

Note - I realize that mutable structs are usually evil, but they seem really useful for this one particular problem.

Upvotes: 6

Views: 296

Answers (2)

Servy
Servy

Reputation: 203817

You can't use this when initializing an instance field, but you can simply initialize it in the constructor to address that.

private Lazy<IEnumerable<int>> _blocks;

public MyClass()
{
   _blocks = new Lazy<IEnumerable<int>>(
         () => this.CalculateBlocks(0).FirstOrDefault());
}

public IEnumerable<int> Blocks
{
    get
    {
        return _blocks.Value;
    }
}

Upvotes: 5

Michael Gunter
Michael Gunter

Reputation: 12811

Just initialize your field in the constructor.

public class MyClass
{
    public MyClass()
    {
        _blocks = new Lazy<IEnumerable<int>>(() => this.CalculateBlocks(0).FirstOrDefault());
    }

    private readonly Lazy<IEnumerable<int>> _blocks;
}

Upvotes: 7

Related Questions