用添加`lazy`关键字C#问题 [英] Problems with adding a `lazy` keyword to C#

查看:123
本文介绍了用添加`lazy`关键字C#问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很想写这样的代码:

 类斑马
{
公共懒INT StripeCount
{
{返回ExpensiveCountingMethodThatReallyOnlyNeedsToBeRunOnce(); }
}
}



编辑:为什么呢?我认为它看起来优于:

 类斑马
{
私人懒< INT> _StripeCount;

公共斑马()
{
this._StripeCount =新的懒惰(()=> ExpensiveCountingMethodThatReallyOnlyNeedsToBeRunOnce());
}

公共懒INT StripeCount
{
{返回this._StripeCount.Value; }
}
}

您致电酒店,它会第一时间。运行 GET 块中的代码,然后将只返回从中值



我的问题:




  1. 将与添加此类关键字的图书馆涉及哪些费用?

  2. 什么情况下会变成这样吗?

  3. 问题你会觉得这有用吗?



我不开始讨伐就可以进入图书馆的下一个版本,但我很好奇像这样的一个功能应该有什么样的考虑,要经过。


解决方案

我很好奇,像这样的一个功能应该有什么样的考虑,要经过。




首先,我写博客有关此主题,在其他之中。看到我的旧博客:



http://blogs.msdn.com / b / ericlippert /



和我的新博客:



http://ericlippert.com



有关语言设计的各个方面的许多文章



二,C#设计过程现在是开放的观点给公众,所以你可以看到自己什么语言设计团队审核新功能的建议时,会考虑。见roslyn.codeplex.com了解详情。




将与添加此类关键字的图书馆涉及哪些费用?




这取决于很多事情。有,当然,没有廉价,方便的特点。只有更便宜,更困难的特性。在一般情况下,成本是那些涉及设计,指定,实现,测试,记录和保持功能。还有更奇特的成本,以及像的不是做更好的功能,或者选择一个功能,与我们可能要添加未来功能不良相互作用成​​本的机会成本。



在这种情况下,该功能很可能会简单地使懒为关键字使用语法糖懒< T> 。这是一个非常简单的功能,不需要很多花哨的语法或语义分析。




什么情况下会变成这样吗?




问题

我能想到的一些会导致我推回功能的因素。



首先,它是没有必要的;它仅仅是一个方便的糖。它并没有真正增加新的电源语言。好处似乎并不值得成本。



其次,更重要的是,它供奉着一个的特别的一种懒惰成语言。有不止一种懒惰的,我们可能会选择错误的。



怎么会有不止一种懒惰的?嗯,想想如何来实现。属性是已经懒在不计算它们的值,直到属性被称为,但你想比这更多;你想有一个被称为一次,然后该值缓存为下一次财产。由懒基本上你的意思是一个memoized属性。我们需要什么样的保证到位?有很多可能性:



可能性#1:不是线程安全的。如果您致电第一时间对两个不同的线程的财产,任何事情都有可能发生。如果你想避免竞争条件,你必须将自己添加同步。



可能性#2:线程安全的,在两个不同的螺纹,使得两次调用属性都调用初始化函数,然后竞赛,看看谁在实际值填充缓存。据推测,该函数将返回两个线程相同的值,所以额外的成本在这里仅仅是在浪费额外调用。但缓存是线程,并且不阻止任何线程。 (因为线程缓存可以具有低锁或无锁码被写入。)



代码以执行线程安全是有代价的,即使它是低锁码。那是成本可以接受吗?大多数人写什么是有效的单线程程序; ?它似乎正确的线程安全的开销添加到每一个懒惰的属性调用无论是需要与否



可能性3:线程安全的,例如,有一个强有力的保障该初始化函数将只被调用一次;有在高速缓存中没有比赛。用户可能有一个隐含的期望,初始化函数只调用一次;它可能是非常昂贵的,在两个不同的线程两个呼叫可能是不可接受的。实现这种懒惰的,需要全同步上它可能是一个线程无限期阻塞而懒惰的方法在另一个线程运行。这也意味着有可能是死锁,如果有一个锁排序问题,懒惰的方法。



这更增加了成本的特点,即由双方平均分担成本人谁做的的利用它(因为他们写单线程程序)。



那么,我们如何面对呢?我们可以添加三个特点:懒不是线程安全的,懒线程与种族和懒线程与封闭,也许死锁。而现在的功能只是得到了一大堆更昂贵和办法的难以记录。这产生的巨大的用户教育问题。每次你给开发者选择这样,你展示他们写可怕的错误的机会。



三,功能似乎弱的说。为什么要被懒惰只适用于属性?看起来这可能一般通过类型系统应用:

 懒INT X = M(); //不调用M()
懒INT Y = X + X; //不添加X + X
INT Z = Y *ÿ; //现在M()被调用一次和缓存。
// X + X的计算和缓存
// Y * y计算

我们尽量不做小,弱的特点,如果有一个更普遍的特点,就是它的一个自然延伸。但是,现在我们谈论真正严重的设计和实施成本。




你会觉得这有用吗?




个人?不是真的有用。我写的很多大都采用Interlocked.Exchange简单的低锁懒代码。 (如果懒惰的方法被运行两次,丢弃的结果之一,我不在乎。我的懒方法是从​​来没有那么贵)的模式很简单,我知道它,也有从未分配给委托额外的对象是安全的或锁,如果我有一些更复杂的我总是可以使用懒< T> 做的工作对我来说。这将是一个小的方便。


I would love to write code like this:

class Zebra
{
    public lazy int StripeCount
    {
        get { return ExpensiveCountingMethodThatReallyOnlyNeedsToBeRunOnce(); }
    }
}

EDIT: Why? I think it looks better than:

class Zebra
{
    private Lazy<int> _StripeCount;

    public Zebra()
    {
        this._StripeCount = new Lazy(() => ExpensiveCountingMethodThatReallyOnlyNeedsToBeRunOnce());
    }

    public lazy int StripeCount
    {
        get { return this._StripeCount.Value; }
    }
}

The first time you call the property, it would run the code in the get block, and afterward would just return the value from it.

My questions:

  1. What costs would be involved with adding this kind of keyword to the library?
  2. What situations would this be problematic in?
  3. Would you find this useful?

I'm not starting a crusade to get this into the next version of the library, but I am curious what kind of considerations a feature such as this should have to go through.

解决方案

I am curious what kind of considerations a feature such as this should have to go through.

First off, I write a blog about this subject, amongst others. See my old blog:

http://blogs.msdn.com/b/ericlippert/

and my new blog:

http://ericlippert.com

for many articles on various aspects of language design.

Second, the C# design process is now open for view to the public, so you can see for yourself what the language design team considers when vetting new feature suggestions. See roslyn.codeplex.com for details.

What costs would be involved with adding this kind of keyword to the library?

It depends on a lot of things. There are, of course, no cheap, easy features. There are only less expensive, less difficult features. In general, the costs are those involving designing, specifying, implementing, testing, documenting and maintaining the feature. There are more exotic costs as well, like the opportunity cost of not doing a better feature, or the cost of choosing a feature that interacts poorly with future features we might want to add.

In this case the feature would probably be simply making the "lazy" keyword a syntactic sugar for using Lazy<T>. That's a pretty straightforward feature, not requiring a lot of fancy syntactic or semantic analysis.

What situations would this be problematic in?

I can think of a number of factors that would cause me to push back on the feature.

First off, it is not necessary; it's merely a convenient sugar. It doesn't really add new power to the language. The benefits don't seem to be worth the costs.

Second, and more importantly, it enshrines a particular kind of laziness into the language. There is more than one kind of laziness, and we might choose wrong.

How is there more than one kind of laziness? Well, think about how it would be implemented. Properties are already "lazy" in that their values are not calculated until the property is called, but you want more than that; you want a property that is called once, and then the value is cached for the next time. By "lazy" essentially you mean a memoized property. What guarantees do we need to put in place? There are many possibilities:

Possibility #1: Not threadsafe at all. If you call the property for the "first" time on two different threads, anything can happen. If you want to avoid race conditions, you have to add synchronization yourself.

Possibility #2: Threadsafe, such that two calls to the property on two different threads both call the initialization function, and then race to see who fills in the actual value in the cache. Presumably the function will return the same value on both threads, so the extra cost here is merely in the wasted extra call. But the cache is threadsafe, and doesn't block any thread. (Because the threadsafe cache can be written with low-lock or no-lock code.)

Code to implement thread safety comes at a cost, even if it is low-lock code. Is that cost acceptable? Most people write what are effectively single-threaded programs; does it seem right to add the overhead of thread safety to every single lazy property call whether it's needed or not?

Possibility #3: Threadsafe such that there is a strong guarantee that the initialization function will only be called once; there is no race on the cache. The user might have an implicit expectation that the initialization function is only called once; it might be very expensive and two calls on two different threads might be unacceptable. Implementing this kind of laziness requires full-on synchronization where it is possible that one thread blocks indefinitely while the lazy method is running on another thread. It also means there could be deadlocks if there's a lock-ordering problem with the lazy method.

That adds even more cost to the feature, a cost that is borne equally by people who do not take advantage of it (because they are writing single-threaded programs).

So how do we deal with this? We could add three features: "lazy not threadsafe", "lazy threadsafe with races" and "lazy threadsafe with blocking and maybe deadlocks". And now the feature just got a whole lot more expensive and way harder to document. This produces an enormous user education problem. Every time you give a developer a choice like this, you present them with an opportunity to write terrible bugs.

Third, the feature seems weak as stated. Why should laziness be applied merely to properties? It seems like this could be applied generally through the type system:

lazy int x = M(); // doesn't call M()
lazy int y = x + x; // doesn't add x + x
int z = y * y; // now M() is called once and cached.
               // x + x is computed and cached
               // y * y is computed

We try to not do small, weak features if there is a more general feature that is a natural extension of it. But now we're talking about really serious design and implementation costs.

Would you find this useful?

Personally? Not really useful. I write lots of simple low-lock lazy code mostly using Interlocked.Exchange. (I don't care if the lazy method gets run twice and one of the results discarded; my lazy methods are never that expensive.) The pattern is straightforward, I know it to be safe, there are never extra objects allocated for the delegate or the locks, and if I have something a little more complex I can always use Lazy<T> to do the work for me. It would be a small convenience.

这篇关于用添加`lazy`关键字C#问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆