0

Im new to C# however im running into an issue when trying to pass the TableOne type class into a method and use within the method itself.

Class:

    public class TableOne
    {
        public List<string> CaseID { get; set; }
        public List<string> Owner { get; set; }
        public List<string> Assignee { get; set; }
        public List<string> Comments { get; set; }
    }

Method:

public static string ComposeHtmlTable<T>(Type classType, IList<T> table)
{
    List<classType> test = table.Cast<classType>().ToList();
    Console.WriteLine(test[0].CaseID[0]); // trying to access data

    return "test";
}

How the method is being called:

ComposeHtmlTable<TableOne>(typeof(TableOne), data.TableOne);

Error im receiving:

'classType' is a variable but is used like a type.

The reason why its imperative that the method uses the parameter type is because there might be multiple types i.e. TableTwo or TableThree that I might pass into that method.

Any ideas on how I can tackle this?

TIA

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
yush
  • 365
  • 9
  • 26
  • Pass it as a _generic type parameter_ in angle-brackets. You don't need `Type` at all. – Dai Dec 27 '21 at 07:03
  • Thanks @Dai, are you able to show how I could do that? I've tried to do a generic type before but I wasn't able to access the List properties – yush Dec 27 '21 at 07:04
  • I just bothered to look at your code now and the _real_ problem is that your `ComposeHtmlTable` method has a bad design. Generic methods (i.e. methods with _generic-type-parameters_) should not be concerned with details of their type-arguments: so your `ComposeHtmlTable` method _should not_ be attempting to look at `test[0].CaseID[0]` in the first place. I don't know why you're doing that in the first place, really - but I don't think you should be using generics here. – Dai Dec 27 '21 at 07:20
  • @ayushlal You want to use generic that have multiple classes,right? https://stackoverflow.com/questions/965580/c-sharp-generics-syntax-for-multiple-type-parameter-constraints – user3682728 Dec 27 '21 at 07:20
  • Thanks @Dai. The reason for that is im trying to create a method that will iterate over the given ```List``` with a given Type and return the HTML output of the iteration – yush Dec 27 '21 at 07:26
  • @ayushlal That's not a problem for which generics are a suitable solution. It sounds like you need to use good ol' fashioned OOP design-patterns for serialization instead: https://stackoverflow.com/questions/6733317/are-there-any-patterns-for-serializing-and-deserializing-object-hierarchies-in-v – Dai Dec 27 '21 at 07:29
  • 1
    @ayushlal Also, a "table" is generally understood to be a _set-of-tuples_ (or just a "list-of-rows"), but your `TableOne` class is a "_tuple-of-lists-of-strings_", which is _not_ a table. – Dai Dec 27 '21 at 07:30
  • Use [where clause](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint) with `IList` or `IList` with either second generic or fixed type (containing `CaseID`). – Eatos Dec 27 '21 at 07:48

3 Answers3

1

Let's have a look at classType and T:

public static string ComposeHtmlTable<T>(Type classType, IList<T> table)
{
    List<classType> test = table.Cast<classType>().ToList();
    Console.WriteLine(test[0].CaseID[0]); // trying to access data

    return "test";
}

Can they be some arbitrary types, say T == int and classType == StringBuider? Definitely not. As I can see, both classType and T should be inherited from TableOne. Let's .net know it:

public static string ComposeHtmlTable<C, T>(IList<T> table) 
  where C : TableOne
  where T : TableOne 
{...}
      

Time to add some details:

  • we don't want IList<T> table as an argument, IEnumerable<T> table is enough (and we will be able to pass not only lists, by, say, arrays)
  • the method is declares as public, any input is possible; so we have to validate input arguments
  • what if test is empty (i.e. it doesn't have any items?). We can't obtain test[0].CaseID[0] in this case.
public static string ComposeHtmlTable<C, T>(IEnumerable<T> table) 
  where C : TableOne
  where T : TableOne 
{
    if (table is null)
        throw new ArgumentNullException(nameof(table));  

    List<C> test = table.Cast<C>().ToList();

    if (test.Count > 0 && test[0] != null && test[0].CaseID.Count > 0)
        Console.WriteLine(test[0].CaseID[0]); // trying to access data  
    else
        Console.WriteLine("test is empty");

   return "test"; 
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
0

The problem is in this line of code List<classType> test = table.Cast<classType>().ToList(); List<classType> and Cast<classType> is invalid syntax. List<T> is acceptable.

Type arguments in c# are sent separately to variables. You've already supplied the type argument T to the generic method. So use List<T> and Cast<T> instead.

Hence passing in classType as a variable is redundant.
Even if you needed to do a switch expression on the the type of elements supplied you can switch on typeof(T)

Shaamil Ahmed
  • 378
  • 2
  • 7
  • Thanks for the suggestion, I have tried to do ```List & Cast``` however I get the following error ```'T' does not contain a definition for 'CaseID' and no accessible extension method 'CaseID' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?``` – yush Dec 27 '21 at 07:12
  • That's another problem: you should work out how to cast between the two types... – Johan Donne Dec 27 '21 at 07:30
0

As @Dai suggest, you could replace the classType parameter by an generic type parameter:

static List<T1> ComposeHtmlTable<T1,T2>(IList<T2> table)
{        
    List<T1> test = table.Cast<T1>().ToList();
    return test;
}

PS: I changed your method to return the new List, since I presume that's what you wanted to achieve...

And call it like:

ComposeHtmlTable<TableOne,TableOne>(data.TableOne);

If the two generic types will always be the same you can rteduce the method to:

static List<T> ComposeHtmlTable<T>(IList<T> table)
{
    return (List<T>) table.Cast<T>().ToList();
}

and call it like:

ComposeHtmlTable<TableOne>(data.TableOne);

Johan Donne
  • 3,104
  • 14
  • 21
  • Thanks for the suggestion, I will try it out shortly. Just for my my knowledge what is the purpose for `````` when calling the method? – yush Dec 27 '21 at 07:29
  • No luck... ```Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List``` – yush Dec 27 '21 at 07:37
  • 1
    @ayushlal: You are passing the two generic types: one for the type of the elements in the returned List and one to specify what kind of IList you will be passing in. If in your application those two will always be the same, you can just stick to one generic Type parameter. I added this to the answer. If you feel the answer is helpfull please upvote it. If it is the right answer to your question, please mark it as correct? – Johan Donne Dec 27 '21 at 07:38
  • 1
    Of course: two be able to use the 'Cast' operator, there should be a cast defined between your two types... – Johan Donne Dec 27 '21 at 07:41
  • Thanks for your help, another quick one. It seems that I cannot access the Properties in the list i.e. ```test[0].CaseID[0])```, I get the following error: ```'T' does not contain a definition for 'CaseID' and no accessible extension method 'CaseID' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?``` – yush Dec 27 '21 at 07:48
  • Inside your generic method the compiler has no knowledge of the properties of the type being passed. So you cannot use them. For debugging purposes: put that statement right after the call to `ComposeHtmlTable`. – Johan Donne Dec 27 '21 at 08:31