C# 在类级变量上使用using语句是一种不好的做法吗?

C# 在类级变量上使用using语句是一种不好的做法吗?,c#,scope,using,C#,Scope,Using,我有类似于下面的代码 class MyController { [ThreadStatic] private DbInterface db; public void ImportAllData() { using (db = new DbInterface()) { var records = PullData(); PushData(records); } }

我有类似于下面的代码

class MyController
{
    [ThreadStatic] private DbInterface db;

    public void ImportAllData()
    {
        using (db = new DbInterface())
        {
            var records = PullData();
            PushData(records);
        }
    }

    private DbRecord[] PullData()
    {
        return db.GetFromTableA();
    }

    private void PushData(DbRecord[] records)
    {
        db.InsertIntoTableB(records);
    }
}
另一种方法维护起来要麻烦得多

class MyController
{
    public void ImportAllData()
    {
        using (var db = new DbInterface())
        {
            var records = PullData(db);
            PushData(records, db);
        }
    }

    private DbRecord[] PullData(DbInterface db)
    {
        return db.GetFromTableA();
    }

    private void PushData(DbRecord[] records, DbInterface db)
    {
        db.InsertIntoTableB(records);
    }
}
在我看来,我的第一个实现:

  • 是线程安全的(假设
    DbInterface
    是线程安全的)
  • 防止任何其他进程接触
    db
    变量,以及
  • 确保始终处置
    db
    ,即使在异常期间也是如此

对具有类作用域的变量使用
using
语句是一种不好的做法吗?我错过什么了吗?

就我个人而言,我更喜欢你的第二个选择

第一种设计的问题在于,您的设计会有效地向设计中添加不必要的耦合。您的
PullData
PushData
方法不能单独使用-它们要求首先调用对
ImportAllData
的调用,或者调用将设置并正确清理
db
变量的其他方法


第二个选项,虽然代码稍微多一些(虽然不多),但使每个方法的意图都非常清楚。每个方法都知道它需要处理传递给它的外部
DbInterface
实例。将来很少或根本不可能被误用。

您的第一个变体将
db
暴露在
使用
块管理的范围之外。这就增加了意外副作用的可能性。例如,另一种方法可能使用或甚至处置db。如果您或以后的维护人员忘记了
db
的隐式契约,或者甚至因为代码输入错误,都可能发生这种情况


我不会使用第一个变体。

这里有一个替代方案:

sealed class MyImporter
{
    private readonly DbInterface db;

    public MyImporter(DbInterface db)
    {
        this.db = db;
    }

    public void ImportAllData()
    {
        var records = PullData();
        PushData(records);
    }

    private DbRecord[] PullData()
    {
        return db.GetFromTableA();
    }

    private void PushData(DbRecord[] records)
    {
        db.InsertIntoTableB(records);
    }
}

在这种情况下,保持引用是类责任的一个明确部分。现在,它也将处置责任推给了用户。这种更明确的构造减少了向“控制器”添加额外功能的诱惑,而从长远来看,这正是第一种方法可能失败的原因。本质上,我们已经将导入函数重构为一个单独的类,这样共享字段访问就不再是一个问题。

这就是构造的目的。我会小心地将using放入属性中,因为约定规定属性访问是轻量级的,并且当用户认为他们只是在访问变量时,不一定要触发create-dispose循环


但是,下面关于代码结构的注释很重要-如果您想导入一次,它将成为用户需要了解的设置步骤。如果可以设想不同的访问模式,依赖项注入设置允许用户控制连接的创建和处理。

是否考虑使用扩展?有趣的是,如果你真的赢了表单绘制代码,OnPaint和Paint事件非常像这样,至少在将ref传递到using块之外。您会收到一个分配给您的图形上下文,它的管理/处理由调用方处理,因此您不会在实际的绘制方法中自行处理它。@DanSaltmer,我计划在其他控制器中使用
DbInterface
类。我希望控制器能够访问所有
DbInterface
方法,但我不希望控制器了解彼此的私有方法。据我所知,扩展方法将适用于所有
DbInterface
实例。还是我误解了你?@tcarvin,我能理解。谢谢您不需要在静态字段上使用
ThreadStatic
?我怀疑第一个变量实际上不是线程安全的;它实际上消除了在类内部进行处理的需要,这也将处理
db
的责任转移给了调用方。这可能是一件好事,也可能是坏事,但这是班级责任的变化。如果调用方不知道如何创建
db
的实例,此模式将不起作用。