0

我想完全像这样从 NHibernate 的 SqlClientBatchingBatcher 类继承(代码取自 带有加密触发器的 TooManyRowsAffectedException):

public class NonBatchingBatcherWithoutVerification : SqlClientBatchingBatcher
{
    public NonBatchingBatcherWithoutVerification(ConnectionManager connectionManager, IInterceptor interceptor) : base(connectionManager, interceptor)
    {}

    protected override void DoExecuteBatch(IDbCommand ps)
    {
        log.DebugFormat("Executing batch");
        CheckReaders();
        Prepare(currentBatch.BatchCommand);
        if (Factory.Settings.SqlStatementLogger.IsDebugEnabled)
        {
            Factory.Settings.SqlStatementLogger.LogBatchCommand(currentBatchCommandsLog.ToString());
            currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
        }

        int rowsAffected = currentBatch.ExecuteNonQuery();

        // Removed the following line
        //Expectations.VerifyOutcomeBatched(totalExpectedRowsAffected, rowsAffected);

        currentBatch.Dispose();
        totalExpectedRowsAffected = 0;
        currentBatch = new SqlClientSqlCommandSet();
    }
}

请注意此处方法中访问的一些成员(如 currentBatch 或 totalExpectedRowsAffected)。

好吧,事实证明这些成员实际上是当前 NHibernate 3.3 源的超类中的私有成员。那么如何在不复制整个内容的情况下有效地继承类呢?顺便说一下,这是该类的未修改的 NHibernate 代码:

 public class SqlClientBatchingBatcher : AbstractBatcher
{
    private int _batchSize;
    private int _totalExpectedRowsAffected;
    private SqlClientSqlCommandSet _currentBatch;
    private StringBuilder _currentBatchCommandsLog;
    private readonly int _defaultTimeout;

    public SqlClientBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
        : base(connectionManager, interceptor)
    {
        _batchSize = Factory.Settings.AdoBatchSize;
        _defaultTimeout = PropertiesHelper.GetInt32(Cfg.Environment.CommandTimeout, Cfg.Environment.Properties, -1);

        _currentBatch = CreateConfiguredBatch();
        //we always create this, because we need to deal with a scenario in which
        //the user change the logging configuration at runtime. Trying to put this
        //behind an if(log.IsDebugEnabled) will cause a null reference exception 
        //at that point.
        _currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
    }

    public override int BatchSize
    {
        get { return _batchSize; }
        set { _batchSize = value; }
    }

    protected override int CountOfStatementsInCurrentBatch
    {
        get { return _currentBatch.CountOfCommands; }
    }

    public override void AddToBatch(IExpectation expectation)
    {
        _totalExpectedRowsAffected += expectation.ExpectedRowCount;
        IDbCommand batchUpdate = CurrentCommand;
        Driver.AdjustCommand(batchUpdate);
        string lineWithParameters = null;
        var sqlStatementLogger = Factory.Settings.SqlStatementLogger;
        if (sqlStatementLogger.IsDebugEnabled || Log.IsDebugEnabled)
        {
            lineWithParameters = sqlStatementLogger.GetCommandLineWithParameters(batchUpdate);
            var formatStyle = sqlStatementLogger.DetermineActualStyle(FormatStyle.Basic);
            lineWithParameters = formatStyle.Formatter.Format(lineWithParameters);
            _currentBatchCommandsLog.Append("command ")
                .Append(_currentBatch.CountOfCommands)
                .Append(":")
                .AppendLine(lineWithParameters);
        }
        if (Log.IsDebugEnabled)
        {
            Log.Debug("Adding to batch:" + lineWithParameters);
        }
        _currentBatch.Append((System.Data.SqlClient.SqlCommand) batchUpdate);

        if (_currentBatch.CountOfCommands >= _batchSize)
        {
            ExecuteBatchWithTiming(batchUpdate);
        }
    }

    protected override void DoExecuteBatch(IDbCommand ps)
    {
        Log.DebugFormat("Executing batch");
        CheckReaders();
        Prepare(_currentBatch.BatchCommand);
        if (Factory.Settings.SqlStatementLogger.IsDebugEnabled)
        {
            Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString());
            _currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
        }

        int rowsAffected;
        try
        {
            rowsAffected = _currentBatch.ExecuteNonQuery();
        }
        catch (DbException e)
        {
            throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command.");
        }

        Expectations.VerifyOutcomeBatched(_totalExpectedRowsAffected, rowsAffected);

        _currentBatch.Dispose();
        _totalExpectedRowsAffected = 0;
        _currentBatch = CreateConfiguredBatch();
    }

    private SqlClientSqlCommandSet CreateConfiguredBatch()
    {
        var result = new SqlClientSqlCommandSet();
        if (_defaultTimeout > 0)
        {
            try
            {
                result.CommandTimeout = _defaultTimeout;
            }
            catch (Exception e)
            {
                if (Log.IsWarnEnabled)
                {
                    Log.Warn(e.ToString());
                }
            }
        }

        return result;
    }
}

我忽略了什么吗?复制整个内容以覆盖对任何私有成员的所有访问权限似乎是一种相当糟糕的方法。我只想覆盖一种方法!

4

4 回答 4

11

只有一种方法可以合法地访问基类的私有成员:将派生类放在基类中:

class Base
{
    private int x;
    private class Derived : Base
    {
        private void M()
        {
            Console.WriteLine(this.x); // legal!
        }
    }
}

当然,如果您可以将类放在基类中,那么您也可以重写基类以保护成员。

原作者将成员设为私有是对您的暗示,该课程不是为您设计的,用于处理这些数据。

于 2013-03-20T21:10:50.723 回答
4

如果它们被设置为私有,那么您真的无能为力(除了使用反射,这很丑陋而且当然并不总是安全)。

于 2013-03-20T20:48:15.803 回答
1

Private无法访问超类的成员,因为它们是private. OOP 中的封装是为了禁止这种直接访问,从而确保对象正常运行。
可能需要properties访问私有成员,这些是您可以用来读取/写入私有成员的成员。这些属性将确保不会对对象造成伤害。

于 2013-03-20T20:50:11.300 回答
1

您可以使用反射访问父类的私有字段、属性和方法(例如,访问此处描述的字段:Reflecting a private field from a base class

这是不安全的,但是私有的想法是库实现可能会改变,那些私有方法、字段和属性可能会改变或消失。如果他们更改实现,更新可能会破坏您的代码。

也就是说,我自己已经做过几次了。你只需要权衡风险。

于 2013-03-20T20:51:01.447 回答