谁处置IDisposable公共财产?如果我有一个实现IDisposable的类SomeDisposableObject:classSomeDisposableObject:IDisposable{publicvoidDispose(){//做一些重要的处理工作。我有另一个名为AContainer的类,它有一个SomeDisposableObject的实例作为公共属性:publicSomeDisposableObjectSomeObject{get{返回m_someObject;}设置{m_someObject=值;然后FxCop会坚持认为AContainer也是IDisposable。这很好,但我不知道如何从AContainer.Dispose()安全地调用AContainer.Dispose(),因为另一个类可能仍然有对m_someObject实例的引用。避免这种情况的最佳方法是什么?(假设其他代码依赖于AContainer.SomeObject始终具有非空值,因此简单地将实例的创建放在AContainer之外不是一种选择)编辑:我将扩展一些示例,因为我认为一些评论者错过了这个问题.如果我只是在调用m_someObject.Dispose()的AContainer上实现一个Dispose()方法,那么我会遇到这些情况://ExampleOneAContainercontainer1=newAContainer();SomeDisposableObjectobj1=container1.SomeObject;container1.Dispose();obj1.DoSomething();//不好,因为obj1已经被container1处理掉了。//示例二AContainercontainer2=newAContainer();SomeObjectobj2=newSomeObject();container2.SomeObject=obj2;//不好,因为SomeObject的前一个值没有被释放。容器2。处置();obj2。做一点事();//不好,因为obj2已经被container2处理掉了,container2并不真正“拥有”它。这有帮助吗?没有一个答案,这取决于您的情况,关键是财产所代表的一次性资源的所有权,正如JonSkeet指出的那样。有时查看.NETFramework中的示例会很有帮助。下面是三个行为不同的例子:另一个有趣的例子是System.DirectoryServices.DirectorySearcher,它有一个读/写的一次性属性SearchRoot。如果这个属性是在外部设置的,底层资源被认为不属于它,所以容器不会处理它。如果它不是从外部设置的,则会在内部生成一个引用,并设置一个标志以确保它将被丢弃。您可以使用LutzReflector看到这一点。您需要确定容器是否拥有该资源,并确保准确记录其行为。如果您确定您拥有该资源,并且该属性是读/写的,则需要确保您的setter处理它正在替换的任何引用,例如:publicSomeDisposableObjectSomeObject{get{returnm_someObject;}set{if((m_someObject!=null)&&(!object.ReferenceEquals(m_someObject,value)){m_someObject.Dispose();}m_someObject=value;}}privateSomeDisposableObjectm_someObject;更新:GrahamS在评论中正确指出最好在处理m_someObject!=value之前在setter中进行测试:我已经更新了上面的示例以考虑到这一点(使用ReferenceEquals而不是!=explicit)。尽管在许多现实世界的场景中,setter的存在可能意味着该对象不属于容器,因此不会被处置。这实际上取决于理论上谁“拥有”一次性物品。在某些情况下,您可能希望能够传入对象,例如一个构造函数,而不是让你的类负责清理它......其他时候你可能想自己清理它。如果你正在创建对象(就像在示例代码中一样),那么清理它几乎肯定是你的责任。至于财产——我认为拥有财产不应该真正转移所有权或类似的东西。如果您的类型负责处理对象,您应该保留该责任。真正的问题可能是您的面向对象设计。如果AContainer是Disposed,它的所有成员对象也应该被释放。如果不是,听起来您可以处理A身体,但想让腿实例保持活动状态。听起来不对。如果您的类中有一次性对象,请使用Dispose方法实现IDisposable,该方法可以处理包装的一次性对象。现在调用代码必须确保配置对象的using()或等效的try/finally代码。我将尝试回答我自己的问题:首先避免它摆脱这种情况的最简单方法是重构您的代码以完全避免该问题。有两种明显的方法可以实现它。外部实例创建如果AContainer不创建SomeDisposableObject的实例,而是依赖外部代码提供它,那么AContainer不再“拥有”该实例并且不负责处置它。可以通过构造函数或设置属性来提供外部创建的实例。公共类AContainerClass{SomeDisposableObjectm_someObject;//这里没有创建。publicAContainerClass(SomeDisposableObjectsomeObject){m_someObject=someObject;}publicSomeDisposableObjectSomeObject{get{returnm_someObject;}设置{m_someObject=值;主要问题是所有权混乱。在Dispose时,AContainer类无法判断谁拥有该实例。它可以是它创建的实例,也可以是外部创建并由属性设置的另一个实例。即使它跟踪它并且确实知道它正在处理它创建的实例,它仍然不能安全地处理它,因为其他类现在可能具有从公共属性获得的引用。如果可以重构代码以避免公开实例(即通过完全删除属性),那么问题就会消失。如果无法避免...如果由于某种原因无法以这些方式重构代码(正如我在问题中指定的那样),那么在我看来,您将面临一些相当困难的设计选择。AlwaysDisposableInstance如果您选择这种方法,那么您实际上是在声明AContainer将在设置属性时取得SomeDisposableObject实例的所有权。这在某些情况下是有意义的,特别是如果SomeDisposableObject显然是一个瞬态或依赖对象。但它应该被仔细地记录下来,因为它要求调用代码知道这种所有权转移。(使用方法而不是属性可能更合适,因为方法名称可以用作所有权的进一步提示)。公共类AContainerClass:IDisposable{SomeDisposableObjectm_someObject=newSomeDisposableObject();publicSomeDisposableObjectSomeObject{get{返回m_someObject;}set{if(m_someObject!=null&&m_someObject!=value)m_someObject.Dispose();m_someObject=值;}}publicvoidDispose(){if(m_someObject!=null)m_someObject.Dispose();GC.SuppressFinalize(这个);}}仅当原始实例仍处于处置状态时实例发生变化,并且仅在原始实例发生时才处置它。这里的所有权模型是混合的。AContainer仍然是它自己的SomeDisposableObject实例的所有者,但如果提供了外部实例,它仍然需要外部代码来处理它。这种方法最能反映这里的现实,但可能难以正确实施。客户端代码仍然可以通过执行以下操作引起问题:AContainerClassaContainer=newAContainerClass();SomeDisposableObjectoriginalInstance=aContainer.SomeObject;aContainer.SomeObject=newSomeDisposableObject();aContainer.DoSomething();aContainer.SomeObject=originalInstance;创建了一个新的实例,一个名为的方法,然后恢复了原来的实例。不幸的是,当AContainer被替换时,它会在原始实例上调用Dispose(),因此它现在没有任何效果。干脆放弃,让GC来处理,这显然不太理想。如果SomeDisposableObject类中确实存在一些稀缺资源,那么不及时处理肯定会出问题。然而,就客户端代码如何与AContainer交互而言,它也可能代表了最强大的方法,因为它不需要知道AContainer如何处理SomeDisposableObject实例的所有权。如果您知道系统上可供您使用的资源实际上并不稀缺,那么这实际上可能是最好的方法。一些评论者建议可以使用引用计数来跟踪是否有任何其他类仍然引用了SomeDisposableObject的实例。这将非常有用,因为它只允许我们在知道这样做是安全的情况下处理它,否则让GC处理它。但我不知道有任何C#/.NETAPI可以确定对象的引用计数。如果是这样,请告诉我。由于缺少封装,您无法在AContainer实例上安全地调用SomeDisposableObject的Dispose()。公共财产提供对部分内部状态的不受限制的访问。由于这部分内部状态必须遵守IDisposable协议的规则,因此确保良好的封装非常重要。该问题类似于允许访问用于锁定的实例。如果这样做,就更难确定从何处获取锁。如果您可以避免暴露您的一次性实例,那么谁将处理对Dispose()的调用的问题也会消失。我遇到的一件有趣的事情是SqlCommand通常拥有一个SqlConnection实例(两者都实现了IDisposable)。但是,在SqlCommand上调用dispose也不会释放连接。我也在Stackoverflow的帮助下找到了这个。换句话说,“子”(嵌套?)实例是否可以/将在以后重用很重要。一般来说,我认为谁创建了对象就应该负责Disposal。在这种情况下,AContainer创建了SomeDisposableObject,因此它应该在AContainer时被Disposed。如果出于某种原因您认为SomeDisposableObject应该比AContainer寿命更长-我只能想到以下方法:总之-我不确定设计是否有意义。毕竟,您似乎期望这样的客户端代码:SomeDisposableObjectd;使用(varc=newAContainer()){d=c.SomeObject;}//dosomethingwithd这似乎破坏了我的客户端代码。它违反了得墨忒耳法则,对我来说是常识。你在这里提到的设计不是可以处理这种情况的东西。你说那个类有一个容器那么它应该自己处理它。如果其他对象可能正在使用它,那么它不是容器的范围并且类扩大了,它需要在该范围的边界处被处理。您可以在Dispose()中标记Disposal。毕竟处置不是析构函数——对象仍然存在。所以:AContainer类:IDisposable{bool_isDisposed=false;publicvoidDispose(){if(!_isDisposed){//处置}_isDisposed=true;}}将此添加到您的其他课程。以上是C#学习教程:谁处置了IDisposable公共属性?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
