【问题标题】:Wrapping a Non-inheritable class in C#在 C# 中包装一个不可继承的类
【发布时间】:2010-01-05 13:24:59
【问题描述】:

我有一个简单的问题。

我想装饰 SqlDataReader 类,以便在调用 dispose 或 close 方法时,我可以同时释放隐藏的资源。

SqlDataReader 类不可继承。

我怎样才能做到这一点?我真的不想实现 DbDataReader、IDataReader、IDisposable 和 IDataRecord 接口

【问题讨论】:

  • 不可继承是什么意思?是密封类吗?你能详细说明一下接口吗?您不必实施这些吗?另外,你为什么不想呢?
  • SqlDataReader 不是密封的,所以它是可继承的,但它的构造函数是私有的,所以当你从它继承时你不能满足基本构造函数 - 我只是在细节上挑剔,但你是对的。
  • 除了我对丝绸的回答的评论之外,我不禁想知道......你可能需要用SqlDataReader 打包什么?我真的希望它不是SqlCommandSqlConnection...
  • 资源是一次性类的实例。它既不是 SqlCommand 也不是 SqlConnection 对象。该资源是来自票证池的票证,用于保护资源(在本例中为 Sql DB)
  • 我讨厌自己这么想,但我想知道除了你的行为之外,是否有办法放置一个 IL 方法挂钩来调用原始的 Close 或 Dispose。我不适合尝试,只是提出一个想法。

标签: c#


【解决方案1】:

即使您可以从 SqlDataReader 继承,也没有关系,因为您无法让 SqlCommand 创建派生类的实例。

当您只是推迟到底层的 SqlDataReader 时,在包装器中实现 IDataReader 并不难。这只是有点耗时,但还不错。

但是我很好奇,你想要的资源是被释放的连接吗?如果是这样,CommandBehavior 枚举的 CloseConnection 成员可确保在关闭数据读取器时关闭连接。

var reader = command.ExecuteReader(CommandBehavior.CloseConnection);
...
reader.Close(); // also closes connection

请注意,在 SqlDataReader 上,Close/Dispose 是一样的。

最后,这是过去对我很有帮助的最后一个建议。请注意,在以下松散示例中,您从头到尾都拥有 SqlDataReader,即使您在每条记录处“让步”回调用者。

private static IEnumerable<IDataRecord> GetResults(this SqlCommand command) {
    using (var myTicket = new MyTicket())
    using (var reader = command.ExecuteReader()) {
        while (reader.Read()) {
            yield return reader;
        }
    }
    // the two resources in the using blocks above will be
    // disposed when the foreach loop below exits
}

...

foreach (var record in myCommand.GetResults()) {

    Console.WriteLine(record.GetString(0));

}

// when the foreach loop above completes, the compiler-generated
// iterator is disposed, allowing the using blocks inside the
// above method to clean up the reader/myTicket objects

【讨论】:

  • 资源是一次性票据,用于保护对 SQL 数据库的访问
  • 好的,我在回答中添加了另一个可能有帮助的建议。
  • 这是一个非常有趣的解决方案。人们对此有何看法?
  • 我非常喜欢 yield 解决方案,因为它能够通过阅读器处理清理多个其他资源。
  • 其实我只是稍微修改了这个例子,让它成为SqlCommand上的一个扩展方法。除了资源处理的东西,foreach 看起来比 while (reader.Read())...
【解决方案2】:

反转它;使用您的“隐藏”资源作为主要内容,实现 IDisposable,然后在完成后关闭 DataReader。

【讨论】:

  • 我真的宁愿调用代码对此一无所知。它当前需要一个 SqlDataReader 实例
  • 您是否拥有调用代码?它真的需要接受SqlDataReader吗?这是你应该尽量不要传递的东西;如果你可以让它接受IDataReader,那么你可以很容易地包装SqlDataReader
  • 是的,我拥有调用代码。我宁愿不必包装类(实现 IDataReader)
  • 哈利:运气不好。重新考虑你的设计,因为你想要的都是不可能的。执着于不可能做到的事情是不合逻辑的。而且你不需要实现IDataReader,我建议你只实现IDisposable。或许您应该详细说明您在做什么,我们可以提供更具体的建议。
  • 这个问题实际上是一个很好的例子,说明为什么共享接口而不是类通常更好,尤其是重系统类 - 您可以轻松地将一个实现替换为另一个,而无需更改太多代码。前期工作多,后期工作少。我想知道消费代码是什么——你甚至可能不需要IDataReader,也许消费者只使用几种方法。抱歉,不是说教的意思,但是silky 说得对,不可能像描述的那样做;是时候重构了。 :-)
【解决方案3】:

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx 类不是密封的。您应该能够在覆盖开始时调用 base.dispose(),然后将代码放在后面。

我面前没有我的 IDE,但它应该看起来像

public myClass : SqlDataReader
{
    protected overide void Dispose(bool disposing) : Base(disposing)
    {
        myCleanupCode();
    }
    protected overide void Dispose()
    {
        myCleanupCode();
    }
    private myCleanupCode()
    {
        //Do cleanup here so you can make one change that will apply to both cases.
    }
}

编辑--- 刚读了原始的 cmets,我看到它有私有构造函数,让我打破我的 VS2008 并且生病了 brb

正在研究它,每个人都在尝试这些奇特的解决方案,我认为唯一可以做的就是

public class myClass : IDisposable
{

    public SqlDataReader dataReader { get; set; }

    #region IDisposable Members

    public void Dispose()
    {
        dataReader.Dispose();
        //My dispose code
    }

    #endregion
}

编辑--- 叹息,这正是 Silky 40 分钟前发布的内容。

【讨论】:

  • 此代码导致编译错误:错误#1 类型“System.Data.SqlClient.SqlDataReader”没有定义构造函数...和...错误#2 'System.Data.SqlClient. SqlDataReader.SqlDataReader(System.Data.SqlClient.SqlCommand, System.Data.CommandBehavior)' 由于其保护级别而无法访问
  • 链接的第一行:“这个类不能被继承。”
  • 还不如传回一个 的元组
  • @Harry 只要 Ticket 实现 Idisposable 这可能是您的最佳选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多