Nic*_*sen 38 c# properties keyword
我想写这样的代码:
class Zebra
{
public lazy int StripeCount
{
get { return ExpensiveCountingMethodThatReallyOnlyNeedsToBeRunOnce(); }
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:为什么?我认为它看起来比:
class Zebra
{
private Lazy<int> _StripeCount;
public Zebra()
{
this._StripeCount = new Lazy(() => ExpensiveCountingMethodThatReallyOnlyNeedsToBeRunOnce());
}
public lazy int StripeCount
{
get { return this._StripeCount.Value; }
}
}
Run Code Online (Sandbox Code Playgroud)
第一次调用属性时,它将运行get块中的代码,然后只返回它的值.
我的问题:
我没有开始讨论如何将其纳入图书馆的下一个版本,但我很好奇这个功能需要经过什么样的考虑.
Eri*_*ert 56
我很好奇这个功能应该经历什么样的考虑.
首先,我写了一篇关于这个主题的博客.看我的旧博客:
http://blogs.msdn.com/b/ericlippert/
和我的新博客:
关于语言设计各方面的许多文章.
其次,C#设计流程现已开放供公众查看,因此您可以自己查看语言设计团队在审核新功能建议时所考虑的内容.有关详细信息,请参阅https://github.com/dotnet/roslyn/.
将这种关键字添加到库中需要花费多少成本?
这取决于很多事情.当然,没有便宜,简单的功能.只有更便宜,更难的功能.通常,成本涉及设计,指定,实施,测试,记录和维护特征.还有更多的异国情调成本,例如没有做更好的功能的机会成本,或者选择与我们可能想要添加的未来功能交互不良的功能的成本.
在这种情况下,该功能可能只是简单地使"懒惰"关键字成为使用的语法糖Lazy<T>.这是一个非常直接的功能,不需要很多花哨的句法或语义分析.
这有什么问题?
我可以想到一些因素会导致我推迟这项功能.
首先,没有必要; 它只是一种方便的糖.它并没有真正为语言增添新的力量.这些好处似乎不值得花费.
其次,更重要的是,它将一种特殊的懒惰包含在语言中.有一种以上的懒惰,我们可能会选择错误.
怎么会有一种以上的懒惰?好吧,想想它将如何实施.属性已经"懒惰",因为在调用属性之前不会计算它们的值,但是你想要的不止于此; 你想要一个被调用一次的属性,然后下次缓存该值."懒惰"基本上是指一个记忆性的财产.我们需要做出哪些保证?有很多可能性:
可能性#1:根本不是线程安全的.如果您在两个不同的线程上"第一次"调用该属性,则可能发生任何事情.如果要避免竞争条件,则必须自己添加同步.
可能性#2:Threadsafe,这样两个不同线程上的属性的两次调用都调用初始化函数,然后竞争以查看谁填写了缓存中的实际值.据推测,该函数将在两个线程上返回相同的值,因此这里的额外成本仅仅是浪费的额外调用.但是缓存是线程安全的,并且不会阻塞任何线程.(因为线程安全缓存可以使用低锁或无锁代码编写.)
实现线程安全的代码是有代价的,即使它是低锁定代码.这个费用可以接受吗?大多数人都会编写有效的单线程程序; 将线程安全的开销添加到每个懒惰的属性调用是否正确是否正确?
可能性#3:Threadsafe使得有一个强有力的保证,初始化函数只会被调用一次; 缓存中没有竞争对手.用户可能有一个隐含的期望,即初始化函数只被调用一次; 它可能非常昂贵,并且两个不同线程上的两个调用可能是不可接受的.实现这种懒惰需要完全同步,其中一个线程可能无限期地阻塞,而惰性方法在另一个线程上运行.这也意味着如果懒惰方法存在锁定排序问题,可能会出现死锁.
这为该功能增加了更多的成本,这个成本由没有利用它的人平等地承担(因为他们正在编写单线程程序).
那我们该怎么处理呢?我们可以添加三个功能:"lazy not threadsafe","lazy threadsafe with races"和"lazy threadsafe with blocking and maybe deadlocks".而现在的功能只是得到了一大堆更昂贵和方式难以记录.这产生了巨大的用户教育问题.每当你给开发人员这样的选择时,你就给他们提供了编写可怕错误的机会.
第三,如上所述,这个特征似乎很弱.为什么懒惰只适用于财产?看起来这通常可以通过类型系统应用:
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
Run Code Online (Sandbox Code Playgroud)
如果有一个更为通用的特征是它的自然延伸,我们尽量不做小而弱的特征.但现在我们谈论的是非常严肃的设计和实施成本.
你觉得这有用吗?
亲身?不太有用.我使用Interlocked.Exchange写了很多简单的低锁懒惰代码.(我不在乎懒惰方法是否运行两次,其中一个结果被丢弃;我的懒惰方法从来都不那么昂贵.)模式很简单,我知道它是安全的,从来没有为委托分配额外的对象或锁,如果我有一些更复杂的东西,我总是可以Lazy<T>用来为我做的工作.这将是一个小便利.
系统库已经有了一个可以满足您需求的类: System.Lazy<T>
我确信它可以集成到该语言中,但正如Eric Lippert将告诉您为某种语言添加功能并不是一件容易接受的事情.必须考虑许多事情,并且利益/成本比率必须非常好.既然System.Lazy已经很好地处理了这个问题,我怀疑我们会很快看到这个.