【问题标题】:How to access private members of superclass in override?如何在覆盖中访问超类的私有成员?
【发布时间】:2013-03-10 04:29:16
【问题描述】:

我想完全像这样从 NHibernate 的 SqlClientBatchingBatcher 类继承(代码取自 TooManyRowsAffectedException with encrypted triggers):

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;
    }
}

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

【问题讨论】:

    标签: c# nhibernate overriding private members


    【解决方案1】:

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

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

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

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

    【讨论】:

      【解决方案2】:

      如果它们被设置为私有,那么你真的没有什么可以做的(除了使用反射,它很丑陋而且当然并不总是安全的)。

      【讨论】:

        【解决方案3】:

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

        【讨论】:

        • 为什么要避免使用使用私有后备变量的属性?
        【解决方案4】:

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

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

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

        【讨论】:

          猜你喜欢
          • 2013-01-09
          • 1970-01-01
          • 1970-01-01
          • 2016-09-06
          • 1970-01-01
          • 2020-10-30
          • 2010-11-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多