Rani
Rani

Reputation: 47

how to set generic list to a pre-existing list of the same type?

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

Answers (5)

Paulo Morgado
Paulo Morgado

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

jakobbotsch
jakobbotsch

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

bubbinator
bubbinator

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

D Stanley
D Stanley

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

Patrick Hofman
Patrick Hofman

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

Related Questions