【问题标题】:Declare generic type parameter as having a destructor?将泛型类型参数声明为具有析构函数?
【发布时间】:2012-01-18 16:43:50
【问题描述】:

我有一个以以下开头的通用类:

public class EntityContextFactory<T>
    where T: class, IDisposable, IObjectContextAdapter, new()

稍后在课堂上,当我有一个包含以下内容的方法时:

            T context = HttpContext.Current.Items[objectContextKey] as T;
            if (context != null)
            {
                context.Dispose();
                GC.SuppressFinalize(context);
                HttpContext.Current.Items.Remove(objectContextKey);
            }

我收到来自 ReSharper 的警告,说 GC.SuppressFinalize 是在没有析构函数的类型上调用的。如何消除此错误?我知道 Dbcontexts 确实有一个析构函数,因为当我非一般地编写这种类型的类时,我没有得到这样的错误。我尝试声明 T 实现与 Dbcontext 相同的接口,但这似乎不起作用......

【问题讨论】:

    标签: asp.net-mvc entity-framework generics ef-code-first destructor


    【解决方案1】:

    您可以通过不调用SuppressFinalize 来消除此错误。没有通用约束(可能也没有 ReSharper 约束)可以应用于要求终结器。

    任何具有终结器的类都应该在其Dispose 中调用GC.SuppressFinalize(this);。如果该类不这样做,则可能有充分的理由,您也不应该调用它。

    【讨论】:

    • +1 - 它的 Dispose() 方法应该调用 GC.SuppressFinalize()
    • 非常正确,先生,没想过要检查,但在查看了 DbContext 的反编译代码后,Dispose 方法确实调用了 Gc.SuppressFinalize(this)。
    【解决方案2】:

    说明

    我不确定您是否需要GC.SuppressFinalize。通常这将用于非托管资源。垃圾收集器 (GC) 对没有 GC.SuppressFinalize 的托管资源执行此操作。 DbContext 是托管资源,您应该 Dispose() 而不是调用 GC.SuppressFinalize

    您应该通过将其范围限制为受保护来防止您的应用程序的用户直接调用对象的 Finalize 方法。

    你可以阅读When should I use GC.SuppressFinalize()?When and How to Use Dispose and Finalize in C#

    更多信息

    【讨论】:

    • 谢谢,尽管在您的示例中它不会做任何事情,因为 EntityContextFactory 是一个通用抽象类,而不是我试图确保有一个解构函数。
    • 我在您写评论的同时更新了我的答案。这是正确的。关键是 DbContext 是托管资源,所以 Dispose 而不是 SuppressFinalize 是正确的方法。
    【解决方案3】:

    首先,GC.SuppressFinalize(obj) 检查给定的引用是否与直接调用方法的 this 指针相等(参见在线帮助:“obj 参数必须是该方法的调用者。”) - 所以GC.SuppressFinalize(this) 以外的任何调用都会抛出异常!

    其次,调用GC.SuppressFinalize(this) 表示GC 不运行终结代码(实例从终结队列中移除)。终结队列越短,垃圾收集器运行得越快……但由于所有对象都继承了终结器……

    第三,调用GC.SuppressFinalize(this) 将有效地阻止任何析构函数运行。我个人认为析构函数是一种代码味道!编写正确的终结器代码绝非易事(请记住,所有引用都可能消失......(已经释放))。除此之外,处理非托管资源应该正确实现IDisposable 并在对象被收集之前调用Dispose()。拥有终结器只是最后一道防线 - 当没有人调用 Dispose() 时。通常应该使用像using() 这样的结构——它会负责调用Dispose()

    有关该主题的详细说明,请参阅Proper use of the IDisposable interface

    所以 - 完全拨打电话 - 如果 - 在哪里拨打电话? 如果您不处理非托管资源并因此没有析构函数,请进行调用。共同点在Dispose() 内部——但这不是必需的。我最喜欢的另一个是 .ctor() - 虽然 this 引用还没有完全构建...

    ReSharper 警告在这里有点误导。它出现在两种情况下——当参数不是调用者时,以及调用不在Dispose() 中时。在您的特殊情况下,您不应该拨打电话。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-11
      • 2019-02-26
      • 2010-10-04
      • 2018-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多