Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Virtual method call in constructor - how to refactor?

Tags:

c#

.net

I have an abstract class for database-agnostic cursor actions. Derived from that, there are classes that implement the abstract methods for handling database-specific stuff.

The problem is, the base class ctor needs to call an abstract method - when the ctor is called, it needs to initialize the database-specific cursor.

I know why this shouldn't be done, I don't need that explanation!

This is my first implementation, that obviously doesn't work - it's the textbook "wrong way" of doing it. The overridden method accesses a field from the derived class, which is not yet instantiated:

public abstract class CursorReader
{
    private readonly int m_rowCount;
    protected CursorReader(string sqlCmd)
    {
         m_rowCount = CreateCursor(sqlCmd); //virtual call !
    }
    protected abstract int CreateCursor(string sqlCmd);

    //...other (non-abstract) methods that assume a cursor exists
}

public class SqlCursorReader : CursorReader
{
    private SqlConnection m_sqlConnection;

    public SqlCursorReader(string sqlCmd, SqlConnection sqlConnection)
    {
        m_sqlConnection = sqlConnection;     //field initialized here
    }
    protected override int CreateCursor(string sqlCmd)
    {
        //uses not-yet-initialized member *m_sqlConnection*
        //so this throws a NullReferenceException
        var cursor = new SqlCursor(sqlCmd, m_sqlConnection); 
        cursor.Create();
        return cursor.Count();
    }
}

I will follow up with an answer on my attempts to fix this...

UPDATE

The overridden method CreateCursor() creates an actual cursor in a database. This is paramount for the correct functioning of many methods that were ommitted from the class.
CreateCursor() must be called in the base ctor in order for the class to be in a consistent state when the ctor returns. I've slightly updated the code above to reflect this.

like image 579
Cristian Diaconescu Avatar asked Mar 16 '26 00:03

Cristian Diaconescu


1 Answers

You could always have a lazy property that gets the count.

public abstract class CursorReader
{
    private int? m_rowCount;
    protected CursorReader()
    {

    }
    protected abstract int CreateCursor(string sqlCmd);
    protected int RowCount {
      get {
          if (m_RowCount == null)
          {
             m_RowCount = CreateCursor(sql);
          }
          return m_RowCount.Value;
      }

    }
}
like image 86
Daniel A. White Avatar answered Mar 17 '26 15:03

Daniel A. White



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!