如何在override中访问超类的私有成员

本文关键字:成员 超类 访问 override | 更新日期: 2023-09-27 18:04:52

我想从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;
    }
}

我忽略了什么吗?复制整个东西只是为了覆盖对任何私有成员的所有访问似乎是一个相当糟糕的方法。我只想重写一个方法!

如何在override中访问超类的私有成员

要合法访问基类的私有成员,只有一种方法:将派生类放入基类中:

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

当然,如果你可以把类放在基类中,那么你也可以重写基类,使成员受到保护。

原作者将成员设为private,这是在暗示您,该类不是为您乱动这些数据而设计的。

如果将它们设置为private,则实际上没有什么可以做的(除了使用反射,这是丑陋的,当然并不总是安全的)。

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

您可以使用反射访问父类的私有字段、属性和方法(例如,访问一个字段如下所述:从基类反射私有字段)

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

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