SitePoint Sponsor

User Tag List

Results 1 to 12 of 12
  1. #1
    SitePoint Guru Chroniclemaster1's Avatar
    Join Date
    Jun 2007
    Location
    San Diego, CA
    Posts
    784
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    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. I've even highlighted the two lines that seem to be throwing the errors.

    Code:
        public class TableCell<ContentType> where ContentType : new()
        {
            private int classRowIndex = 0;
            private int classColumnIndex = 0;
            private ContentType classTableCellContent = new ContentType();
            
            
            /// <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<ContentType>(int inputRowIndex, int inputColumnIndex, ContentType inputTableCellContent)
            {
                classRowIndex = inputRowIndex;
                classColumnIndex = inputColumnIndex;
                classTableCellContent = inputTableCellContent;
            }
        }
    Whatever you can do or dream you can, begin it.
    Boldness has genius, power and magic in it. Begin it now.

    Chroniclemaster1, Founder of Earth Chronicle
    A Growing History of our Planet, by our Planet, for our Planet.

  2. #2
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Code Csharp:
    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
            }
        }
    }

  3. #3
    SitePoint Guru Chroniclemaster1's Avatar
    Join Date
    Jun 2007
    Location
    San Diego, CA
    Posts
    784
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Serenarules View Post
    Code Csharp:
        // 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
    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...

    Quote Originally Posted by Serenarules View Post
    Code Csharp:
        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
            }
        }
    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\to\file\MyCsFile.cs(332)]' to 'T [e:\webroot\App_Code\path\to\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;"
    Whatever you can do or dream you can, begin it.
    Boldness has genius, power and magic in it. Begin it now.

    Chroniclemaster1, Founder of Earth Chronicle
    A Growing History of our Planet, by our Planet, for our Planet.

  4. #4
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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
    };

  5. #5
    SitePoint Guru Chroniclemaster1's Avatar
    Join Date
    Jun 2007
    Location
    San Diego, CA
    Posts
    784
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thank you! Got it working!

    Quote Originally Posted by Serenarules View Post
    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.
    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.
    Whatever you can do or dream you can, begin it.
    Boldness has genius, power and magic in it. Begin it now.

    Chroniclemaster1, Founder of Earth Chronicle
    A Growing History of our Planet, by our Planet, for our Planet.

  6. #6
    SitePoint Guru Chroniclemaster1's Avatar
    Join Date
    Jun 2007
    Location
    San Diego, CA
    Posts
    784
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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?
    Whatever you can do or dream you can, begin it.
    Boldness has genius, power and magic in it. Begin it now.

    Chroniclemaster1, Founder of Earth Chronicle
    A Growing History of our Planet, by our Planet, for our Planet.

  7. #7
    SitePoint Zealot
    Join Date
    May 2004
    Location
    Jersey
    Posts
    175
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Chroniclemaster1 View Post
    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?
    Might find this useful: http://stackoverflow.com/questions/3...es-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.
    Matt Daly

  8. #8
    SitePoint Guru Chroniclemaster1's Avatar
    Join Date
    Jun 2007
    Location
    San Diego, CA
    Posts
    784
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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...

    Code:
    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.
    Whatever you can do or dream you can, begin it.
    Boldness has genius, power and magic in it. Begin it now.

    Chroniclemaster1, Founder of Earth Chronicle
    A Growing History of our Planet, by our Planet, for our Planet.

  9. #9
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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.

  10. #10
    SitePoint Guru Chroniclemaster1's Avatar
    Join Date
    Jun 2007
    Location
    San Diego, CA
    Posts
    784
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

    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.)?
    Whatever you can do or dream you can, begin it.
    Boldness has genius, power and magic in it. Begin it now.

    Chroniclemaster1, Founder of Earth Chronicle
    A Growing History of our Planet, by our Planet, for our Planet.

  11. #11
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    This might work even better for you. I included the full code with sample usage.

    Code Csharp:
    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);
            }
        }
    }

  12. #12
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    You can also go with something smaller and more concrete:

    Code Csharp:
     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.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •