【问题标题】:Why is FxCop raising the error "Types that own disposable fields should be disposable" on a class with no disposable fields?为什么 FxCop 在没有一次性字段的类上引发错误“拥有一次性字段的类型应该是一次性的”?
【发布时间】:2009-08-02 15:08:34
【问题描述】:

我有一个 LINQ 对象,其中添加了一个附加方法。该类没有一次性属性或方法,但 FxCop 引发错误“拥有一次性字段的类型应该是一次性的”并引用该类。

我已经将代码减少到这里,但仍然收到错误:

partial class WikiPage
{
    public PagePermissionSet GetUserPermissions(Guid? userId) {
        using (WikiTomeDataContext context = new WikiTomeDataContext()) {
            var permissions =
                from wiki in context.Wikis
                from pageTag in context.VirtualWikiPageTags
                select new {};

            return null;
        }
    }
}

但是,如果我删除了 from 子句中的任何一个,FxCop 就会停止给出错误:

partial class WikiPage
{
    public PagePermissionSet GetUserPermissions(Guid? userId) {
        using (WikiTomeDataContext context = new WikiTomeDataContext()) {
            var permissions =
                from pageTag in context.VirtualWikiPageTags
                select new {};

            return null;
        }
    }
}

或者

partial class WikiPage
{
    public PagePermissionSet GetUserPermissions(Guid? userId) {
        using (WikiTomeDataContext context = new WikiTomeDataContext()) {
            var permissions =
                from wiki in context.Wikis
                select new {};

            return null;
        }
    }
}

PagePermissionSet 不是一次性的。

这是误报吗?或者 LINQ 代码是否以某种方式在类上生成了一次性字段?如果不是误报,FxCop 建议我实现 IDisposable 接口,但是在 Dispose 方法中我会做什么?

编辑: 完整的 FxCop 错误是:

“在 'WikiPage' 上实现 IDisposable,因为它 创建以下 IDisposable 类型的成员: 'WikiTomeDataContext'。如果 'WikiPage' 以前有 已发货,添加了实现 IDisposable 的新成员 这种类型被认为是对现有的重大更改 消费者。”

编辑 2: 这是引发错误的反汇编代码:

public PagePermissionSet GetUserPermissions(Guid? userId)
{
    using (WikiTomeDataContext context = new WikiTomeDataContext())
    {
        ParameterExpression CS$0$0001;
        ParameterExpression CS$0$0003;
        var permissions = context.Wikis.SelectMany(Expression.Lambda<Func<Wiki, IEnumerable<VirtualWikiPageTag>>>(Expression.Property(Expression.Constant(context), (MethodInfo) methodof(WikiTomeDataContext.get_VirtualWikiPageTags)), new ParameterExpression[] { CS$0$0001 = Expression.Parameter(typeof(Wiki), "wiki") }), Expression.Lambda(Expression.New((ConstructorInfo) methodof(<>f__AnonymousType8..ctor), new Expression[0], new MethodInfo[0]), new ParameterExpression[] { CS$0$0001 = Expression.Parameter(typeof(Wiki), "wiki"), CS$0$0003 = Expression.Parameter(typeof(VirtualWikiPageTag), "pageTag") }));
        return null;
    }
}

编辑 3: 似乎确实有一个包含对 DataContext 的引用的闭包类。这是它的反汇编代码:

[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
    // Fields
    public WikiTomeDataContext context;

    // Methods
    public <>c__DisplayClass1();
}

【问题讨论】:

    标签: linq idisposable fxcop


    【解决方案1】:

    我的猜测是,这两个 From 子句会生成对 SelectMany 的调用,并在您的数据上下文中关闭。闭包的实例有一个指向 datacontext 的字段,这会导致 FxCop 警告。这没什么好担心的。

    您的数据上下文只有一个实例,您可以通过 using 块对其进行清理。由于闭包没有终结器,因此 FxCop 警告中没有性能或安全含义。

    【讨论】:

    • 这听起来很合理。如果是这种情况,我是否需要采取某种措施来确保 DataContext 已被释放?
    • 没什么好担心的。您的数据上下文只有 1 个实例,您可以通过 using 块对其进行清理。 b/c 闭包没有终结器,这里没有性能暗示。
    【解决方案2】:

    我注意到这是一个部分类。您是否检查了该类的其他实现文件并查看它是否具有未释放的 IDisposable 成员?

    我认为生成的闭包在这里没有错。闭包是使用某些属性生成的,这些属性会导致 FxCop 忽略这样的警告。

    编辑

    OP 的进一步调查表明,这是一个 IDisposable 字段被提升为闭包的问题。

    不幸的是,您对此无能为力。没有办法使闭包实现 IDisposable。事件如果可以,则无法在闭包实例上调用 IDisposable。

    解决此问题的最佳方法是重写代码,使可支配值不会在闭包中捕获。一次性字段应始终在完成后处理,并在闭包中捕获它会阻止您这样做。

    【讨论】:

    • 如果我删除上述方法,FxCop 不会产生错误。我还查看了该类的反汇编代码,它似乎没有任何类级变量的 IDisposable 属性。
    • @AaronSieb,很有趣。你有没有检查过生成的闭包类,看看它的任何成员是否实现了 IDisposable
    • @JaredPar 我该怎么做?我对闭包的概念有点模糊......反汇编代码(通过.NET Reflector)已附加到我的问题的末尾,如果有帮助的话。
    • @AaronSieb,查看类型 WikiPage,其中应该嵌套了几个内部类型。它的名称为 DisplayClass,名称的两侧都带有看似随机的字符。这是“闭包类”。
    • @JaredPar Closure 类确实存在,确实有一个字段 DataContext 字段,并且没有实现 IDisposable。我已将反汇编代码附加到问题的末尾。我需要做什么才能正确清理它?
    【解决方案3】:

    如果您从方法返回 LINQ 查询,消费者将使用 foreach 迭代结果。

    当使用者完成一个 foreach 循环时,它会在内部调用 IEnumerable 源(在本例中为您的 LINQ 查询)上的 dispose。这将处理 WikiTomeDataContext。

    但是,如果消费者调用了返回 LINQ 查询的方法,但从未迭代结果,则 enumerable 似乎永远不会被释放(也就是说,直到垃圾收集器清理对象)。这将导致您的 WikiTomeDataContext 在垃圾收集之前不会被释放。

    解决此问题的一种方法是对 LINQ 查询的结果调用 .ToArray,对上下文调用 dispose,然后返回数组。

    【讨论】:

    • 这有帮助。从示例代码中不清楚,但不会返回 LINQ IEnumerable。如果在 GC 运行时 DataContext 得到正确处理,那确实让我的担心少了一点。
    【解决方案4】:

    您给出错误的代码使用了 WikiDataContext。

    您没有给出错误的两个示例使用 WikiTomeDataContext。

    这两者之间可能存在一些差异导致错误。

    【讨论】:

    • 不错,但似乎并非如此。我已经编辑了我的问题以使用一致的示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-04
    • 1970-01-01
    相关资源
    最近更新 更多