TicTacToe endgame feedback in C#
Naming

The enum Cell defines Empty, Circle and Cross. This values aren't cells in terms of an object, they are more like types.

For example: Empty is not a cell, but a cell can be empty

You can rename Cell to CellType to make it more clear.

Comments

Comments are a good documentation. You did a great job with the &lt;summary&gt;-tag. But the comments like // Row 1, // Row 2, // Clm 3 are not so good..

Robert C. Martin, who wrote the book "Clean Code" and many more, sad


  Don’t Use a Comment When You Can Use a Function or a Variable


Introduce Methods

We can wrap the if-statements into methods

bool IsFirstRowComplete(CellType cellType) {
    if (this.cells[0, 0] == cellType &amp;&amp; this.cells[0, 1] == cellType &amp;&amp; this.cells[0, 2] == cellType) {
        return true;
    }
    return false;
}

bool IsSecondRowComplete(CellType cellType) {
    if (this.cells[1, 0] == cellType &amp;&amp; this.cells[1, 1] == cellType &amp;&amp; this.cells[1, 2] == cellType) {
        return true;
    }
    return false;
}

// ...


Make them less complex

Since these methods return a Boolean we can return the boolean-expression itself

bool IsFirstRowComplete(CellType cellType) {
    return this.cells[0, 0] == cellType
           &amp;&amp; this.cells[0, 1] == cellType
           &amp;&amp; this.cells[0, 2] == cellType
}

bool IsSecondRowComplete(CellType cellType) {
    return this.cells[1, 0] == cellType
           &amp;&amp; this.cells[1, 1] == cellType
           &amp;&amp; this.cells[1, 2] == cellType
}

// ...


Remove Duplication

The new methods IsFirstRowComplete and IsSecondRowComplete looks very similar. We can extract the main logic and wrap it into its own method.

bool IsRowComplete(CellType cellType, int row) {
    return this.cells[row, 0] == cellType
           &amp;&amp; this.cells[row, 1] == cellType
           &amp;&amp; this.cells[row, 2] == cellType
}

// ...


If you like you can now wrap this method into IsFirstRowComplete and so on

bool IsFirstRowComplete(CellType cellType, int row) {
    return IsRowComplete(cellType, 0);
}

// ...


Introduce more Methods

We could finish here.. But we can make the code more clear if we wrap each check into a method

bool IsARowComplete(CellType cellType) {
    return IsFirstRowComplete(cellType, 0)
           || IsSecondRowComplete(cellType, 1)
           || IsThirdRowComplete(cellType, 2);
}

bool IsAColumnsComplete(CellType cellType) {
  return IsFirstColumnComplete(cellType, 0)
         || IsSecondColumnComplete(cellType, 1)
         || IsThirdColumnComplete(cellType, 2);
}

// ...


After the Refactoring

bool IsGameOver(CellType cellType) {
    return IsARowComplete(cellType)
           || IsAColumnComplete(cellType)
           || IsAHorizontalComplete(cellType)
}


Maybe a Bug

If the method isGameOver gets called with a CellType of type Empty it could return true

So maybe you need to check if the cellType is not Empty