shearichard
shearichard

Reputation: 8372

C# generic class contains list of <T> but I cannot access methods of the <T> class

I have a Generic class,DisplayGrid ...

class DisplayGrid<T> where T : class 

... which has a List of <T> as a property.

public List<T> allRows;

... within a property of DisplayGrid I want to iterate over allRows and access a method of <T> ...

foreach (T r in this.allRows)
{
  if (!(r.isAValidRow))
  {
    blnRowsValid = false;
    break;
  }
}

... however attempting to make use of the isAValidRow method defined on <T> generates a compiler error ...

error CS1061: 'T' does not contain a definition for 'isAValidRow' and no extension method 'isAValidRow' accepting a first argument of type 'T' could be found

Reading back through old questions it seems this area has changed over the years. It seems unlikely that such a basic requirement would still be impossible ?


EDIT 1 : In the original version of this question I perhaps went too far in trying to simplify the sample code. I only included a single Row class but what I meant to imply is that there were multiple classes which might be used as a 'Row' when instantiating the DisplayGrid class.

I have now adapted the classes below so that there are two different classes which might be used as the <T> of the DisplayGrid class.

In doing this I hope to address a number of comments which, correctly, suggested there wasn't any need to use Generics, or that the 'where' directive could be limited to the only class, Row, which I had supplied in the example.

In the changes to the example I have defined classes RowA and RowB and I do appreciate that as shown they are functionally identical but this is just to make a simple example - in actual use RowA and RowB would be sufficiently distinctive to justify their not being the same class.


Full Classes

All Both classes referenced above appear below :

class DisplayGrid<T> where T : class  
{
    public List<T> allRows;

    public DisplayGrid()
    {
        this.allRows = new List<T>();
    }

    public bool hasRowMsgs 
    {
        get
        {
            bool blnRowsValid = true;
            foreach (T r in this.allRows)
            {
                if (!(r.isAValidRow))
                {
                    blnRowsValid = false;
                    break;
                }
            }
            return blnRowsValid;
        }
    }
    public bool isAValidTable
    {
        get {
            if (this.hasRowMsgs == false)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
class RowA
{
    public List<string> rowMsgs;

    public RowA()
    {
        rowMsgs = new List<string>();
    }

    public bool isAValidRow
    {
        get {
            return (this.rowMsgs.Count == 0) ? true : false;
        }
    }
}
class RowB
{
    public List<string> rowMsgs;

    public RowB()
    {
        rowMsgs = new List<string>();
    }

    public bool isAValidRow
    {
        get {
            return (this.rowMsgs.Count == 0) ? true : false;
        }
    }
}

Upvotes: 0

Views: 96

Answers (2)

shearichard
shearichard

Reputation: 8372

After having read, and understood the significance of, the comment by Lee I now have a working version. The key to making it work was defining an Interface, IRow and applying it to the 'Row' classes, RowA and RowB.

For the sake of future readers here are the classes which now work.

Two points

  • I have changed the code a little from the previous example code within the properties/methods to make them more succinct and to resolve an unrelated defects;
  • RowA and RowB are unrealistically similar, this is only to allow the issue to be illustrated simply and briefly, clearly in a real implementation they would be less similar.

class DisplayGrid<T> where T : IRow 
{
    public List<T> allRows;

    public DisplayGrid()
    {
        this.allRows = new List<T>();
    }

    public bool hasRowMsgs 
    {
        get
        {
            bool blnHasRowMsgs = false;
            foreach (T r in this.allRows)
            {
                if (!(r.isAValidRow))
                {
                    blnHasRowMsgs = true;
                    break;
                }
            }
            return blnHasRowMsgs;
        }
    }
    public bool isAValidTable
    {
        get {
            return (!(this.hasRowMsgs));
        }
    }
}
interface IRow
{
    bool isAValidRow { get; }
}
public class RowA : IRow 
{
    public List<string> rowMsgs;

    public bool isAValidRow {
        get {
            return this.rowMsgs.Count > 0 ? false : true;
        }
    }

    public RowA()
    {
        rowMsgs = new List<string>();
    }
}
public class RowB : IRow 
{
    public List<string> rowMsgs;

    public bool isAValidRow {
        get {
            return this.rowMsgs.Count > 0 ? false : true;
        }
    }

    public RowB()
    {
        rowMsgs = new List<string>();
    }
}

Upvotes: 2

Eva423
Eva423

Reputation: 53

If you change the first line to

class DisplayGrid<T> where T : Row  

then it should work.

Explanation: The method you are trying to use is a property of a Row; it is not a method of 'class' so it is not recognized.

Upvotes: 0

Related Questions