如何在lambda表达式中使用IDisposable?

3
我正在对一个项目进行代码分析,并实现有意义的建议。其中一个建议是执行以下操作:

CA2000:Microsoft.Reliability:在方法'Service.ParseConfigurationFile()'中,调用System.IDisposable.Dispose释放对象'new SecureString()',并使所有对它的引用超出范围

有问题的行如下:
 Password = me.Password.Aggregate(new SecureString(), (secureString, c) => { secureString.AppendChar(c); return secureString; })

有没有关于如何正确处理这个的想法?我已经用下面的行替换了上面的行,但我认为它并不正确,因为它仍然会导致代码分析消息出现:

Password = me.Password.Aggregate(new SecureString(), (secureString, c) => { using (secureString) {secureString.AppendChar(c); return secureString;} })

编辑:根据下面@Jon的评论,objectInstance是一个名为MailboxElement(me)的自定义类实例。它正在通过配置文件中的多个自定义部分,其格式如下:

foreach (MailboxElement me in mailboxesSection.Mailboxes)
{
      MailboxInformation mailboxInformation = new MailboxInformation
      {
                    ExchangeServerWebServiceUrl = me.ExchangeServerWebServiceUrl,
                    MailboxFriendlyName = me.FriendlyName,
                    UserName = me.UserName,
                    Password = me.Password.Aggregate(new SecureString(), (secureString, c) => { secureString.AppendChar(c); return secureString; }),
                    MailboxToAccess = me.MailboxToAccess
      };

      // Do stuff with mailboxInformation here
}

MailboxElement是一个密封类,实现了ConfigurationElement,具有上述所有属性。

MailboxInformation定义如下:

public class MailboxInformation
{
   public string MailboxFriendlyName { get; set; }
   public string UserName { get; set; }
   public SecureString Password { get; set; }
   public string ExchangeServerWebServiceUrl { get; set; }
   public string MailboxToAccess { get; set; }
   public string InboxFolderId { get; set; }
   public string SentItemsFolderId { get; set; }
   public bool MailboxSettingsDiscovered { get; set; }
}

我希望这能使事情更清晰明了...


1
以下是一些不错的参考资料:http://blog.linqexchange.com/index.php/how-to-use-idisposable-with-linq/ - Jocke
如果您能说明objectInstance.Password是什么以及这段代码的目的,那将会很有帮助。 - Jon
1
这行代码之后,你对Password做的任何操作都可能会导致什么情况?LINQ本身不应该导致任何丢失的SecureStrings,所以要么代码分析器不够智能以意识到这一点,要么你正在放弃Password - Rawling
“Password”是当前实例的一个字段(可能是一个属性)吗? - Maghis
2
这是一个 CA2000 的误报。 - Alex
我在我的代码中加入了以下抑制项: [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000: Dispose objects before losing scope. This is a broken rule. See https://dev59.com/WXE85IYBdhLWcg3wnU-p for more")] - noonand
3个回答

3

您从聚合中返回了一个IDisposable对象,因此您需要处理它或将其重新赋值给secureString。

using (var secureString = new SecureString()) {
    secureString = objectInstance.Password.Aggregate((secureString, c) => { secureString.AppendChar(c); return secureString; }){
}

编辑 根据问题更新

由于您的MailboxInformation对象现在拥有一个IDisposable,因此MailboxInformation本身应该是IDisposable,并且应处置它拥有的可处理对象。

因此实现方式将类似于您的注释:

public class MailboxInformation : IDisposeable
{
   //...
   public SecureString Password { get; set; }
   //...
   void IDisposable.Dispose() {
      this.Password.Dispose();
   }
}

由于它是对象初始化程序的一部分,使得问题变得复杂。请参见扩展问题。 - noonand
1
我实现了IDisposable接口,在Dispose()方法中调用了this.Password.Dispose();吗? - noonand
@noonand,我更新了一个示例实现。由于分析器中的错误,警告可能仍然存在,但是您已经解决了当前的问题,但是由于您将任务推给使用 MailboxInformaiton 的人,现在应该会收到更多警告。不过这是一件好事,因为系统现在能够识别缺失的处理程序(以及某些误报)。 - Rune FS
谢谢你,正如你所猜测的那样,错误仍然存在。这意味着@Rajesh Subramanian的答案是正确的,即规则被打破了。我感谢你花时间写下这篇文章。 - noonand
1
@noonand 但别忘了查看那些实例化MailboxInformation对象的地方,因为你需要处理它们,并注意你的设计可能会在多个对象之间共享IDisposable并且每个对象都拥有所有权。 - Rune FS
1
在你所展示的代码中(无论是否采纳上述建议),警告是正确的,因此除非你已经修复了所有有效的原因,否则你想知道在你的情况下是否存在一些无效的原因。 - Rune FS

1

那么底线是我什么也做不了吗?我应该使用属性来抑制错误消息吗? - noonand
2
@noonand 在你的特定情况下,警告是正确的。你正在将IDisposable分配给一个类型的成员,而该类型本身不可处置,因此当最后一个引用超出范围时,你将拥有一个不会被处置的IDisposable引用。仅仅因为有时可以忽略警告并不意味着你应该总是忽略它。在忽略之前,请使用你的分析能力来确定是否存在问题。 - Rune FS

1

尝试使用这种格式:

using (var secureString = new SecureString()) {
    Password = objectInstance.Password.Aggregate([...])
}

我认为最好将整个内容放在一个 using 语句中。


“secureString”在整个聚合过程中都被使用,因此“using”应该在其外部。此外,使用“secureString”的方式应在“using”语句中,在聚合之外。 - SimpleVar
@RajeshSubramanian 在这种情况下,可以忽略警告。Sam 提出了正确的处理方法。 - SimpleVar
是的,没错。但这仍然不是解决问题的方法。 - Rajesh Subramanian
@RajeshSubramanian,恐怕我没有测试过。而且我一直认为继承IDisposable的类应该包含在using语句中。我猜这个工具坏了就很难知道什么时候用了 :P - Samuel Parkinson
由于它是对象初始化程序的一部分,因此变得更加复杂。请参见扩展问题。 - noonand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接