Storing data in a generic field

I’m having a couple basic generics problems. Basically, I’m trying to identify a piece of content by two indexes.

  1. I need to store the content in the class, so I need to create a class variable, classTableCellContent, to store it in. Therefore I need to create one, requiring me to constrain the generic type “where ContentType : new()”. However, when I attempt to consume this type with a string, e.g. TableCell<string>, it throws a compiler error “The type ‘string’ must have a public parameterless constructor in order to use it as a parameter blahblahblah…”

  2. I need to accept input, so I have an add function. However, when I try to indicate that the third parameter is the same data type, i.e. ContentType, the compiler seems to believe that I’m creating a “new” data type with a poorly chosen name that hides the original data type declared in the class declaration. I’m sure this must be something stupid I’m missing. All I want to do is accept an input variable of the generic type in a function and store it in the class for later use.

Here’s the code. :blush::eye::x I’ve even highlighted the two lines that seem to be throwing the errors.


    public class TableCell<ContentType> where ContentType : new()
    {
        private int classRowIndex = 0;
        private int classColumnIndex = 0;
     [U][I][B]   private ContentType classTableCellContent = new ContentType();[/B][/I][/U]
        
        
        /// <summary>
        /// TableCell<ContentType>() - Constructors for creating a new TableCell
        /// </summary>
        public TableCell() {}
        public TableCell(int inputRowIndex, int inputColumnIndex, ContentType inputTableCellContent)
        {
            addContent<ContentType>(inputRowIndex, inputColumnIndex, inputTableCellContent);
        }
        
        
        /// <summary>
        /// void addContent<ContentType>(int inputRowIndex, int inputColumnIndex, ContentType inputTableCellContent) - accepts row and column indexesa dictionary and returns the XHTML to display all it's contents as a list
        /// </summary>
        public void addContent<[B][I][U]ContentType[/U][/I][/B]>(int inputRowIndex, int inputColumnIndex, ContentType inputTableCellContent)
        {
            classRowIndex = inputRowIndex;
            classColumnIndex = inputColumnIndex;
          [U][I][B]  classTableCellContent = inputTableCellContent;[/B][/I][/U]
        }
    }


You can also go with something smaller and more concrete:

 public class TableCell
{
public int RowIndex { get; protected set; }
public int ColumnIndex { get; protected set; }
public object CellContent { get; protected set; }
public Type ContentType { get; protected set; }
public TableCell(int rowIndex, int columnIndex, object cellContent, Type contentType)
{
RowIndex = rowIndex;
ColumnIndex = columnIndex;
CellContent = cellContent;
ContentType = contentType;
}
 
}

And set it like this:

var tc = new TableCell(1, 2, “string”, typeof(string));

Anyway, the point is, there’s a lot of ways to acheive this.

This might work even better for you. I included the full code with sample usage.

using System.Collections.Generic;
namespace Cells
{
    
    public abstract class TableCell
    {
    
        public int RowIndex { get; set; }
        public int ColumnIndex { get; set; }
        public virtual object CellContent { get; set; }
 
    }
    public class StringTableCell : TableCell
    {
        public string CellContent
        {
            get { return base.CellContent as string; }
            set { base.CellContent = value; }
        }
    }
    public class IntTableCell : TableCell
    {
        public int CellContent
        {
            get { return int.Parse(base.CellContent.ToString()); }
            set { base.CellContent = value; }
        }
    }
    public class BoolTableCell : TableCell
    {
        public bool CellContent
        {
            get { return bool.Parse(base.CellContent.ToString()); }
            set { base.CellContent = value; }
        }
    }
    public class TableCellCollection : List<TableCell> { }
    public class Test
    {
        public Test()
        {
            var tcs = new TableCellCollection();
            var sts = new StringTableCell() { CellContent = "Hi" };
            var its = new IntTableCell() { CellContent = 42 };
            var bts = new BoolTableCell() { CellContent = false };
            tcs.Add(sts);
            tcs.Add(its);
            tcs.Add(bts);
        }
    }
}

Might find this useful: http://stackoverflow.com/questions/353126/c-multiple-generic-types-in-one-list

I didn’t read the whole thread but the question looks identical to yours. Edit: There’s also two further topics (one with more replies) under the ‘Linked’ heading to the right.

OK, architectural question. Now that my cell elements are working, I need a way to group them and pass them to a UI class that will create the HTTP response.

However, the entire reason for creating them as generics is to use them with multiple data types. Cell<string>, Cell<int>, Cell<CustomType1>, Cell<CustomType2>, etc.

So is there a way to box those things up together? When I try to drop a Cell<string> and a Cell<int> into a List or something, that clearly doesn’t work. I tried implementing an ICell interface, and in architectural applications, that worked fine, but eventually in the run time code I had to create a List<T> which got grumpy. I don’t really want to have to convert everything into string, I want to support the native data type.

Alternatively, the generics prohibit me from being able to truly hide the implementation of the inner workings of the cells, the data type they are wrapping inside is explicitly stated each time the class is consumed “Cell<string> newCell = new Cell<string>();” Is there a way to revamp the cell classes so that they provide better information hiding and I can consume the Cell class, an interface, or something similar?

:slight_smile: Thank you! Got it working!

Clearly not. I missed that. Once I removed the generics on the addContent() method, it had no place to look for the T variable except the class declaration.

“ContentType” was simply the last version of the name of the open generic type. Now that it’s up and running, I switched it back to my preferred TContent. So that’s why I’m using generics, so I can leverage any object and index it in two dimensions. I may add a constraint later to ensure a proper output method, but that’s what was going on there.

using System.Collections.Generic;
using System.Net.Mime;
namespace Chroniclemaster
{
    // declare a generic using placeholder...
    public class TableCell<T>
        // ...which must be, or derived from ContentType
        // no need to mark it as newable since we're
        // passing in the intended value
        where T : ContentType
    {
 
        private int classRowIndex = 0;
        private int classColumnIndex = 0;
        private T classTableCellContent;
        // the above doesn't need to be newed
        // since it shouldn't have a value
        // untill you assign one
        public TableCell() { }
        // in this method, mark the third param as T (our generic placeholder)
        public TableCell(int inputRowIndex, int inputColumnIndex, T inputTableCellContent)
        {
            addContent(inputRowIndex, inputColumnIndex, inputTableCellContent);
        }
        // in this method, mark the third param as T (our generic placeholder)
        public void addContent(int inputRowIndex, int inputColumnIndex, T inputTableCellContent)
        {
            classRowIndex = inputRowIndex;
            classColumnIndex = inputColumnIndex;
            classTableCellContent = inputTableCellContent;
            // your data is now saved in this class instance
        }
    }
}

Interesting. I will have to look at that for awhile. That’s pretty clever. It does highlight the point, that whatever goes in has to come out as a string, or you have to be very careful to match the data types up. This is probably the best variant I’ve seen, certainly the most ingenious. :slight_smile:

It still leaves me concerned that we’re fundamentally trading off either information hiding (because the data type isn’t hidden) or preventing the data from being modified (because the data type IS hidden). I wonder if this class is going to need to be a UI class only (and then just convert the data to a string and forget generics, etc.)?

The consensus seems to be what I thought. You can use interfaces to make the architecture work right, but no one seems to have cracked the problem of the runtime code where you need to write something like…

multiDataContainer.add(List<string>);
multiDataContainer.add(List<CustomDataType>);

I really just want to be able to build containers which look the same from the outside, provide a common interface that can be used by program architecture, so that run time code simply involves operating on the container class, then shoving it in a controller function and the container is swept away through the architecture and what you want gets done. That part is (relatively speaking) easy.

The hard part is creating well-designed containers. If I don’t use generics then every single different data type that might go in would need to coded separately. Having code in triplicate for strings, ints, and doubles, doesn’t thrill me, not to mention any other simple or custom data types (my projects can have a dozen(s) custom objects. Yet if I use generics, then consuming the generic gives away the underlying data type and breaks the ability to perform real information hiding. I’m starting to think it may be a catch-22.

Ok, I have a revision for you then. Instead of doing your generics at the top level, do it at the method level. This is just an example…

public class TableCell
{
private object cellData;

// pass any value in…
public void SetData(object data)
{
cellData = data;
}

// and get a typed value back
public T GetData<T>()
{
return cellData as T;
}

}

Now you can do this:

// get a new cell
var tc = new TableCell();

// store a string
tc.SetData(“hello”);

// get the string back
string greeting = tc.GetData<string>();

// store an int
tc.SetData(42);

// get the int back
int value = tc.GetData<int>();

And finally, your collection class…

public class TableCellCollection : List<TableCell>
{
}

Just be careful, if you store a value as one type, and fetch it as another, it may not always cast. It would be better to write your own storage class. This was just a simple example.

Just curious, did you try copy-n-paste of what I had instead of just altering your own code?

In regards to the line: addContent<T>(int inputRowIndex, int inputColumnIndex, T inputTableCellContent)".

You don’t need the <T> after the method name since T is already declared at the class level.

Show me your exact TableCell class code and a snippet of how you are using it. The cast error seems to indicate that what you’re passing isn’t a ContentType, or derived from it.

I’m not entirely certain why you are trying to use a generic here. The following would work just as well, unless you have multiple classes you need it to work against.

public class TableCell
{

public int RowIndex { get; set; }
public int ColumnIndex { get; set; }
public ContentType TableCellContent { get; set; }

}

Then just assign them values:

var tc = new TableCell
{
RowIndex = 42,
ColumnIndex = 84,
TableCellContent = someobjectbasedoncontenttype
};

:rofl::rofl: :lol::lol: :rofl::rofl: Duh, it works like an interface. OK, that’s two errors down and I can pass strings into it. I’m still having problems with the second issue though…

OK, I’ve changed all these references from ContentType to T, but it’s still throwing virtually the same error message.

CS0029: Cannot implicitly convert type ‘T [e:\webroot\App_Code\path\ o\file\MyCsFile.cs(332)]’ to ‘T [e:\webroot\App_Code\path\ o\file\MyCsFile.cs(305)]’” Where 305 is the line number where I declare my class “TableCell<T>” and 332 is where I declare the addContent() function “addContent<T>(int inputRowIndex, int inputColumnIndex, T inputTableCellContent)”. I’ve tried changing either or both sets of parameters to other names, but it continues to throw this error saying it can’t execute “classTableCellContent = inputTableCellContent;”