C# “我真的不应该这样做吗?”;“长跑”;属性访问器中的内容?
如果是,为什么? 什么是“长跑” 在属性访问器中使用魔法似乎是我作为类设计者的特权。我一直在想,这就是为什么C#的设计师把这些东西放在那里——这样我就可以做我想做的事情 当然,最好的做法是尽量减少对类用户的意外,因此在方法中嵌入真正长时间运行的东西(例如,10分钟的蒙特卡罗分析)是有意义的C# “我真的不应该这样做吗?”;“长跑”;属性访问器中的内容?,c#,vb.net,properties,class-design,C#,Vb.net,Properties,Class Design,如果是,为什么? 什么是“长跑” 在属性访问器中使用魔法似乎是我作为类设计者的特权。我一直在想,这就是为什么C#的设计师把这些东西放在那里——这样我就可以做我想做的事情 当然,最好的做法是尽量减少对类用户的意外,因此在方法中嵌入真正长时间运行的东西(例如,10分钟的蒙特卡罗分析)是有意义的 但假设prop访问器需要db read。我已经打开了数据库连接。在属性访问器中,db访问代码是否在正常预期范围内是“可接受的”?在属性访问器中读取db是可以的-这实际上是延迟加载的全部要点。我认为最重要的是将
但假设prop访问器需要db read。我已经打开了数据库连接。在属性访问器中,db访问代码是否在正常预期范围内是“可接受的”?在属性访问器中读取db是可以的-这实际上是延迟加载的全部要点。我认为最重要的是将其记录好,以便该类的用户了解在访问该属性时可能会出现性能问题。我不知道这有什么问题,只要您提供XML文档,这样Intellisense就可以通知对象的消费者他们正在做什么
我认为这是一种没有正确答案的情况。我的座右铭是“说总是几乎总是错的”。在任何特定情况下,你都应该做最有意义的事情,而不考虑广义的概括。你可以做任何你想做的事情,但你应该记住API的消费者。访问器和变异器(getter和setter)的重量应该非常轻。基于这种期望,使用您的API的开发人员可能会频繁地对这些属性进行聊天调用。如果在实现中使用外部资源,可能会出现意外的瓶颈 为了保持一致性,最好遵守公共API的约定。如果您的实现完全是私有的,那么可能没有什么坏处(除了私下解决问题的方法与公开解决问题的方法不一致)。不让属性访问器花费很长时间来执行只是一种“良好做法”。 这是因为属性看起来像调用者的字段,因此调用者(即API的用户)通常认为只有一个“return smth”
如果你真的需要一些幕后的动作,考虑创建一个方法…
,就像你提到的,这对这个类的用户来说是个惊喜。人们习惯于用财产做这样的事情(人为的例子如下:)
这看起来很自然,但如果每次访问数据库时,item.Value
实际上都会击中数据库,这将是一个小灾难,应该以与此等效的方式编写:
foreach (var item in bunchOfItems)
{
var temp = item.Value;
foreach (var slot in someCollection)
slot.Value = temp;
}
请帮助指导使用您的代码的人远离像这样的隐患,并将缓慢的事情放在方法中,让人们知道他们是缓慢的
当然,也有一些例外。只要延迟加载不会花费太长的时间,那么延迟加载就可以了,而且有时使things属性对于反射和数据绑定相关的原因来说非常有用,所以您可能希望改变这一规则。但是,如果没有特定的理由,违反惯例和违背人们的期望,那就没有多大意义了。在属性getter中访问数据库是可以的,但是尝试通过缓存值来限制数据库被命中的次数 很多时候,人们在循环中使用属性而不考虑性能,因此您必须预测这种使用。程序员在多次使用某个属性时,并不总是存储该属性的值
如果对这段数据可行,则将从数据库返回的值缓存在私有变量中。通过这种方式,访问通常非常快。除了已经发布的好答案之外,我还要补充一点,当您检查类的实例时,调试器会自动显示属性值。您真的希望在每次检查类时调试代码并在调试器中执行数据库获取吗?善待代码的未来维护者,不要这样做
此外,《框架设计指南》中也广泛讨论了这个问题;考虑拿起一个拷贝。 这与你的问题没有直接关系,但是你是否考虑过一次加载一个方法和一个刷新参数的组合?
class Example
{
private bool userNameLoaded = false;
private string userName = "";
public string UserName(bool refresh)
{
userNameLoaded = !refresh;
return UserName();
}
public string UserName()
{
if (!userNameLoaded)
{
/*
userName=SomeDBMethod();
*/
userNameLoaded = true;
}
return userName;
}
}
+1:我最近在一个属性访问器中被一个bug咬了一口,它发生了大约1000001次故障。它“就像”一个断开连接的属性,实际上应该是一个TryConnect函数(或多或少,很难将特定领域的内容转换成注释俳句)@Binary-Worrier:Unexpected things/导致无数痛苦/在使用代码时。+1,非常清楚和明显的例子。此外,还强调了这样一个事实,即将数据访问放在属性中会妨碍执行批处理更新和在事务中包装更新,而无需将此复杂功能构建到类本身中。我的典型模式是实例化和检索(如果是nec)值一次,然后将其缓存为专用字段。第一次通过一个db访问,第一次检索后没有db访问,除非其他内容发生了变化。我认为这很好,因为只要多次访问成本不高,就不会像本例中那样有任何误解的风险。在使用此策略时,请小心不一致的读取。除了悲观锁定之外,您只有很少的选项可以防止在执行此路线时出现一致性问题。根据业务问题的不同,这可能不是一个问题,但如果是,最好咬紧牙关,提前加载数据访问。根据框架设计指南,第一版,第118页,“长时间运行”意味着“比
class Example
{
private bool userNameLoaded = false;
private string userName = "";
public string UserName(bool refresh)
{
userNameLoaded = !refresh;
return UserName();
}
public string UserName()
{
if (!userNameLoaded)
{
/*
userName=SomeDBMethod();
*/
userNameLoaded = true;
}
return userName;
}
}