简体   繁体   English

缓存属性:更简单的方法?

[英]Cached Property: Easier way?

I have a object with properties that are expensive to compute, so they are only calculated on first access and then cached.我有一个对象的属性计算起来很昂贵,所以它们只在第一次访问时计算然​​后缓存。

 private List<Note> notes;
 public List<Note> Notes
    {
        get
        {
            if (this.notes == null)
            {
                this.notes = CalcNotes();
            }
            return this.notes;
        }
    }

I wonder, is there a better way to do this?我想知道,有没有更好的方法来做到这一点? Is it somehow possible to create a Cached Property or something like that in C#?是否有可能在 C# 中创建缓存属性或类似的东西?

As far as syntax goes, you can use the null-coalescing operator if you want to be fancy, but it's not necessarily as readable.就语法而言,如果您想花哨,可以使用空合并运算符,但它不一定具有可读性。

get
{
    return notes ?? (notes = CalcNotes());
}

Edit: Updated courtesy of Matthew.编辑:更新礼貌马修。 Also, I think the other answers are more helpful to the question asker!另外,我认为其他答案对提问者更有帮助!

In .NET 3.5 or earlier, what you have is a very standard practice, and a fine model.在 .NET 3.5 或更早版本中,您拥有的是一个非常标准的实践和一个很好的模型。

(Although, I would suggest returning IList<T> , or IEnumerable<T> if possible, instead of List<T> in your public API - List<T> should be an implementation detail...) (虽然,我会建议返回IList<T>IEnumerable<T>如果可能的话,而不是List<T>的公共API中- List<T>应该是一个实现细节...)

In .NET 4, however, there is a simpler option here: Lazy<T> .但是,在 .NET 4 中,这里有一个更简单的选项: Lazy<T> This lets you do:这让您可以:

private Lazy<IList<Note>> notes;
public IEnumerable<Note> Notes
{
    get
    {
        return this.notes.Value;
    }
}

// In constructor:
this.notes = new Lazy<IList<Note>>(this.CalcNotes);

Looks pretty standard to me.对我来说看起来很标准。 What you are doing is fine.你在做什么很好。

The problem with ??问题是?? is that if CalcNotes() returns null then it will not be cached any more.是如果CalcNotes()返回null那么它将不再被缓存。 Ditto for value types if for example both 0 and NaN are allowed as property value.如果例如0NaN都允许作为属性值,则值类型同上。

Much better would be an "aspect-oriented" solution, something like Post - Sharp does using attributes and then modifying MSIL (bytecode).更好的是“面向方面”的解决方案,例如Post - Sharp使用属性然后修改 MSIL(字节码)。

The code would look like:代码如下所示:

[Cached]
public List<Note> Notes { get { return CalcNotes(); } }

EDIT: CciSharp.LazyProperty.dll does exactly this!!!编辑: CciSharp.LazyProperty.dll正是这样做的!!!

Yes it is possible.是的,这是可能的。 The question is, how much you are winning by doing it - you still need the initialization code somewhere, so at most you will be saving the conditional expression.问题是,这样做你赢了多少——你仍然需要在某处初始化代码,所以你最多只能保存条件表达式。

A while ago I implemented a class to handle this.不久前,我实现了一个类来处理这个问题。 You can find the code posted in this question , where I ask whether it's a good idea.您可以在这个问题中找到发布的代码,我在那里询问这是否是一个好主意。 There are some interesting opinions in the answers, be sure to read them all before deciding to use it.答案中有一些有趣的意见,请务必在决定使用之前阅读所有内容。

Edit:编辑:

A Lazy<T> class that does basically the same as my implementation that I link to above, has been added to the .NET 4 Framework;一个Lazy<T> 类与我上面链接的实现基本相同,已添加到 .NET 4 框架中; so you can use that if you are on .NET 4. See an example here: http://weblogs.asp.net/gunnarpeipman/archive/2009/05/19/net-framework-4-0-using-system-lazy-lt-t-gt.aspx因此,如果您使用的是 .NET 4,则可以使用它。请参见此处的示例: http : //weblogs.asp.net/gunnarpeipman/archive/2009/05/19/net-framework-4-0-using-system-懒人-lt-t-gt.aspx

If the value is non-trivial to compute, I generally prefer using a method ( GetNotes() ).如果该值难以计算,我通常更喜欢使用方法 ( GetNotes() )。 There's nothing stopping you from caching the value with a method, plus you can add the [Pure] attribute (.NET 4) if applicable to indicate the method does not alter the state of the object.没有什么可以阻止您使用方法缓存值,此外,如果适用,您可以添加[Pure]属性 (.NET 4) 以指示该方法不会改变对象的状态。

If you do decide to stay with the following, I recommend:如果您确实决定保留以下内容,我建议:

Whenever you have a lazily-evaluated property, you should add the following attribute to ensure that running in the debugger behaves the same as running outside of it:每当您有一个延迟评估的属性时,您应该添加以下属性以确保在调试器中运行的行为与在调试器之外运行的行为相同:

[DebuggerBrowsable(DebuggerBrowsableState.Never)]

Also, starting with .NET 4, you can use the following:此外,从 .NET 4 开始,您可以使用以下内容:

// the actual assignment will go in the constructor.
private readonly Lazy<List<Note>> _notes = new Lazy<List<Note>>(CalcNotes);

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public List<Note> Notes
{
    get { return _notes.Value; }
}

问题是陈旧的,但这是你在2019年的表现 - 默认值的自动属性(C#6.0及更高版本)

public List<Note> Notes { get; private set; } = CalcNotes();

A two-line option with C# 8 and null-coalescing assignment :带有 C# 8 和空合并赋值的两行选项:

private List<Note>? notes;
public List<Note> Notes => notes ??= CalcNotes();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM