2013-03-20 86 views
0

我想从NHibernate的SqlClientBatchingBatcher类继承的正是这样(从TooManyRowsAffectedException with encrypted triggers采取代码):如何在override中访问超类的私有成员?

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

如果它们被设置为私人的,那么你可以做的事情真的没有(使用反射,这是丑陋的,当然不总是安全的)。

1

Private超类的成员无法访问,因为它们是private。封装在OOP中是为了禁止这个直接访问,所以确保对象正常工作。
可能有properties访问私有成员,这些是您可以用来读写私人成员的私钥。这些属性将确保不会对对象造成伤害。

+0

为什么你不要使用使用私人支持变量的属性? – Blorgbeard 2013-03-20 20:51:26

+0

我表达了自己的一点不清楚,现在会改变... – 2013-03-20 20:52:16

1

您可以访问私有字段,属性和使用反射父类的方法(例如,访问一个字段说明如下:Reflecting a private field from a base class

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

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

11

只有一个合法访问基类的私有成员的方法:把派生类的基类中:

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

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

原始作者将这些成员设为私人对您而言是暗示该课程并非专门为您设计的。