Home > Back-end >  Generic list of generic lists
Generic list of generic lists

Time:11-26

I've been playing with various ways of using generics and have hit a road block.

Consider the following classes:

public abstract class DataElement
{
    public int Id { get; set; }
}

public class School : DataElement
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}
    
public class Course : DataElement
{
    public int Id { get; set; }
    public int SchoolId { get; set; }
    public string Name { get; set; } = string.Empty;
}
    
public class Student : DataElement
{
    public int Id { get; set; }
    public int CourseId { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Phone { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
}

Considering a hypothetical scenario where none of this data changes, I'm trying to create a DataDictionary class to house those objects in their respective Lists all within one top-level List property. This crazy idea came to me, when I was writing code to load the different data types from JSON files. I was able to write one load method that could read all three types of data using generics, and that sent me down this particular rabbit hole.

public interface IDataDictionary
{
    public List<T> GetAllItemsFromList<T>();
    public T GetSingleItemFromList<T>(int id);
}

public class DataDictionary : IDataDictionary
{
    public List<IList> Data = new List<IList>();

    // Return all objects from the list of type T
    public List<T> GetAllItemsFromList<T>()
    {
        return Data.OfType<T>().ToList();  // This works, returning the appropriate list.
    }

    // Return specific object from the list of type T by Id property value
    public T GetSingleItemFromList<T>(int id)
    {
        List<T> list = Data.OfType<List<T>>().First();  // This works, resolving to the correct list (e.g. Courses).
        return list.Where(i => i.Id == id).First();  // This doesn't work. It doesn't appear to know about the Id property in this context.
    }
}

GetAllItemsFromList works fine, returning the appropriate list of items

List<School> newList = GetAllItemsFromList<School>();

However, I am unable to figure out how to return a single item by Id from its respective list.

School newSchool = GetSingleItemFromList<School>(1);

It could be that I'm just trying to be too clever with this, but I can't help but think there is a way to do this, and I'm just missing it.

CodePudding user response:

This doesn't work. It doesn't appear to know about the Id property in this context.

Yes, cause you have not specified any constraints to the type parametr T, so it can be anything. There are multiple options:

  • Create an interface IHaveId and constraint T to it (or use DataElement):
public interface IHaveId
{
    int Id { get; set; }
}

public interface IDataDictionary
{
    public List<T> GetAllItemsFromList<T>();
    public T GetSingleItemFromList<T>(int id) where T : IHaveId; // or where T : DataElement
}

public class DataDictionary : IDataDictionary
{
    public T GetSingleItemFromList<T>(int id) where T : IHaveId // or where T : DataElement
    {
        List<T> list = Data.OfType<List<T>>().First(); 
        return list.Where(i => i.Id == id)
            .First(); 
    }
}
  • add additional parameter to select the id:

      public T GetSingleItemFromList<T>(int id, Func<T, int> idSelector)
      {
          List<T> list = Data.OfType<List<T>>().First(); 
          return list.First(i => idSelector(i) == id);
      }
    
  • use reflection - I would argue the least recommended option

CodePudding user response:

The problem is that T parameter in function GetSingleItemFromList<T>(int id) could be anything. In order of this to work you should add a constraint:

GetSingleItemFromList<T>(int id) where T: DataElement

  • Related