Reputation: 47
I am using c# and have 4 existing lists, each of a different type (i.e. List<Doctor>
, List<Patient>
etc')
I have a generic search method which receives type T and should search using LINQ the appropriate list based on the type T.
I created a var List<T> listToSearch
and wanted to set it to the appropriate list using if's, but I can't set listToSearch
to any of them.
Code:
// at class level:
private List<Doctor> doctorList;
private List<Patient> patientList;
private List<Visit> visitList;
private List<Treatment> treatmentList;
public IEnumerable search<T>(string field, string rangeStart, string rangeEnd = null)
{
List<T> listToSearch = null;
if (typeof(T) == typeof(Doctor)) { listToSearch = doctorList; }
if (typeof(T) == typeof(Patient)) { listToSearch = patientList; }
if (typeof(T) == typeof(Visit)) { listToSearch = visitList; }
if (typeof(T) == typeof(Treatment)) { listToSearch = treatmentList; }
// more code that isn't relevant to the question here
}
Each typeof(T)
line brings up an error:
"Cannot implicitly convert type
'System.Collections.Generic.List<Doctor/Patient/Visit/Treatment>'
to'System.Collections.Generic.List<T>'
How do I change this code to allow for the use of Generic lists?
Upvotes: 0
Views: 120
Reputation: 14846
If you were, at least, returning IEnumerable<T>
I could understand using the type parameter, but what you are doing here is reinventing method overloading.
Try this:
public IEnumerable<Doctor> SearchDoctors(string field, string rangeStart, string rangeEnd = null)
{
return Search(doctorList, field, rangeStart, rangeEnd);
}
public IEnumerable<Patient> SearchPatients(string field, string rangeStart, string rangeEnd = null)
{
return Search(patientList, field, rangeStart, rangeEnd);
}
public IEnumerable<Visit> SearchVisits(string field, string rangeStart, string rangeEnd = null)
{
return Search(visitList, field, rangeStart, rangeEnd);
}
public IEnumerable<Treatment> SearchTreatments(string field, string rangeStart, string rangeEnd = null)
{
return Search(treatmentList, field, rangeStart, rangeEnd);
}
private IEnumerable<T> Search<T>(IEnumerable<T> list, string field, string rangeStart, string rangeEnd)
{
// more code that isn't relevant to the question here
}
By the way, are you aware that default argument values are hardcode in the caller after compilation?
Consider changing to this:
public IEnumerable<Doctor> SearchDoctors(string field, string rangeStart)
{
return Search(doctorList, field, rangeStart);
}
public IEnumerable<Doctor> SearchDoctors(string field, string rangeStart, string rangeEnd)
{
return Search(doctorList, field, rangeStart, rangeEnd);
}
public IEnumerable<Patient> SearchPatients(string field, string rangeStart)
{
return Search(patientList, field, rangeStart);
}
public IEnumerable<Patient> SearchPatients(string field, string rangeStart, string rangeEnd)
{
return Search(patientList, field, rangeStart, rangeEnd);
}
public IEnumerable<Visit> SearchVisits(string field, string rangeStart)
{
return Search(visitList, field, rangeStart);
}
public IEnumerable<Visit> SearchVisits(string field, string rangeStart, string rangeEnd)
{
return Search(visitList, field, rangeStart, rangeEnd);
}
public IEnumerable<Treatment> SearchTreatments(string field, string rangeStart)
{
return Search(treatmentList, field, rangeStart);
}
public IEnumerable<Treatment> SearchTreatments(string field, string rangeStart, string rangeEnd)
{
return Search(treatmentList, field, rangeStart, rangeEnd);
}
private IEnumerable<T> Search<T>(IEnumerable<T> list, string field, string rangeStart, string rangeEnd = null)
{
// more code that isn't relevant to the question here
}
Upvotes: 0
Reputation: 6337
You can cast it to a List<T>
by casting it to an object first:
if (typeof(T) == typeof(Doctor)) { listToSearch = (List<T>)(object)doctorList; }
Upvotes: 1
Reputation: 495
I ran into something like this before I understood what generics are for. In my case I was trying to reduce the number of methods that were needed to add data to a handler before writing it as an xml
file which isn't too far from what you are trying to accomplish. I was trying to reduce the number of exposed methods from 8 to 1. I ended up using an interface instead of a generic.
In short, you probably can get your desired functionality by using an interface
instead of a generic
. D Stanley is correct. Write code that works, then improve. That way you can try something with the option of eliminating the changes to restore the functionality. Also, Eric Lippert wrote on the subject on Generics (the post is in stack overflow, I just can't find it right now) that if you write a method for using a generic and immediately throw in logic statements to sort out what the object type is, then you are using generics wrong.
Upvotes: 0
Reputation: 152521
The reason it doesn't work is because T
is not known at compile time. You are asking to take a list of a known type and use it as a list of an unknown type, which is not allowed (without dynamics or some other non-compile-time-type-safe mechanism).
Since you're only supporting 4 types anyways, it sounds like you need four search
methods that call a generic common method (if appropriate):
public IEnumerable<Doctor> searchDoctors(string field, string rangeStart, string rangeEnd = null)
{
List<Doctor> listToSearch = doctorList;
// more code that isn't relevant to the question here
}
public IEnumerable<Patient> searchPatients(string field, string rangeStart, string rangeEnd = null)
{
List<Patient> listToSearch = patientList;
// more code that isn't relevant to the question here
}
public IEnumerable<Visit> searchVisits(string field, string rangeStart, string rangeEnd = null)
{
List<Visit> listToSearch = visitList;
// more code that isn't relevant to the question here
}
public IEnumerable<Treatment> searchTreatments(string field, string rangeStart, string rangeEnd = null)
{
List<Treatment> listToSearch = treatmentList;
// more code that isn't relevant to the question here
}
otherwise you're going to have a lot of code validating/casting/converting types that is susceptible to runtime errors.
Side note:
Since you are new to C# - I would recommend not trying to optimize/refactor too much using generics, etc. Write code that works (even if it's using copy-paste, not DRY, etc.), then make it better. Otherwise you spend a lot more time trying to shoehorn your program into some pattern that thinking about how the program should work.
Upvotes: 2
Reputation: 156948
The thing you are asking is not possible.
I would recommend to type your listToSearch
as IList
. This will keep as much generic as you want. You can access all common list actions and you don't have to rely on generics.
IList listToSearch = null;
Upvotes: 0