Reputation: 569
I am trying to use generics to reduce my codebase, but ran into this situation. I can't seem to successfully create a google query that expresses what I am trying to do.
In essence I am passing in a Generic to make a List<T>
and then pass that List<T>
into a function that requires a List<SpecificClass>
My JSONUser class
public class JSONUser
{
public string DocumentId { get; set; }
[JsonProperty(PropertyName = "UserName", Required = Required.Always)]
public string UserName { get; set; }
[JsonProperty(PropertyName = "FirstName", Required = Required.AllowNull)]
public string FirstName { get; set; }
[JsonProperty(PropertyName = "LastName", Required = Required.AllowNull)]
public string Lastname { get; set; }
}
Assume there is a class JSONCompany with company like fields.
Main Code:
static void CollectUserData()
{
boolean j = GetJSON<JSONUser>();
boolean k = GetJSON<JSONCompany>();
...
}
static boolean GetJSON<T>()
{
...
// Get the JSON in a List
List<T> oJSON = CallRest<T>();
// Now depending on the type passed in, call a different
// Process Function which expects a List<JSONUser> or
// List<JSONCompany> parameter
if (typeof(T) == typeof(JSONUser))
{
boolean result = ProcessUser(oJSON);
...
}
else if (typeof(T) == typeof(JSONCompany))
{
boolean result = ProcessCompany(oJSON);
...
}
...
}
public boolean ProcessUser(List<JSONUser> JSONList)
{
...
}
public boolean ProcessCompany(List<JSONCompany> JSONList)
{
...
}
Everything is good until I call the ProcessUser(oJSON);
It says there is no method to accept the Generic. When I try to cast it, it says
Cannot Covert Type
System.Collection.Generic.List<T>
toSystem.Collection.Generic.List<JSONUser>
Hopefully this is clear.
Upvotes: 5
Views: 4059
Reputation: 15275
As OP has indicated that the Process functions need to operate on the lists, here is a second answer to complement my first answer. Based on feedback from OP, this assumes that the purpose of ProcessUser/ProcessCompany was really meant to bulk-upload the list of users/companies to a database, which I will assume to be SQL. In this case, JSONUser and JSONCompany (and his other classes) would likely require different SQL calls to function correctly.
Thus, this code demonstrates iterating over the generic list, still using Interface on the base class to define how that object gets written to the database, and yet still not relying on typeof(T).
public interface IInsertToSql
{
void InsertToSql(SqlCommand cmd);
}
public class JSONUser : IInsertToSql
{
public string UserID { get; set; }
public void InsertToSql(SqlCommand cmd)
{
cmd.CommandText = "sp_insertJsonUser";
cmd.Parameters.AddWithValue("@jsonUserId", UserID);
// More parameters
cmd.ExecuteNonQuery();
}
}
public class JSONCompany : IInsertToSql
{
public string CompanyID { get; set; }
public void InsertToSql(SqlCommand cmd)
{
cmd.CommandText = "sp_insertJsonCompany";
cmd.Parameters.AddWithValue("@jsonCompanyId", CompanyID);
// More parameters....
cmd.ExecuteNonQuery();
}
}
void Main()
{
List<JSONUser> users = GetJSON<JSONUser>();
List<JSONCompany> companies = GetJSON<JSONCompany>();
BulkUpload(users);
BulkUpload(companies);
}
static void BulkUpload<T>(List<T> list)
where T: IInsertToSql
{
using(SqlConnection conn = new SqlConnection(""))
{
conn.Open();
foreach(T t in list)
{
using (SqlCommand cmd = conn.CreateCommand()) t.InsertToSql(cmd);
}
}
}
static List<T> GetJSON<T>()
{
// OP's code to get the list of items, instead of...
return new List<T>();
}
Upvotes: 1
Reputation: 152624
If ProcessUser
et al does not require a list and can work with just an IEnumerable<T>
then you can simplify it a bit:
public boolean ProcessUser(IEnumerable<JSONUser> JSONList)
{
...
}
public boolean ProcessCompany(IEnumerable<JSONCompany> JSONList)
{
...
}
Then just call it with:
boolean result = ProcessUser(oJSON.Cast<JSONUser>());
otherwise you could create a new list:
boolean result = ProcessUser(oJSON.Cast<JSONUser>().ToList());
which may be fine if you're just iteerating/modifying the objects in the list and not the list itself. (adding/removing/sorting/etc.)
Upvotes: 1
Reputation: 15275
Having specific functions to do specific things to specific generic types is generally against the design idea of generics. If you are checking typeof(T) then this is a good indicator (but not necessarily a perfect one) that there's probably a better design.
There are several ways to accomplish what I think you are trying to do... here's one example to get your brain moving in the right direction:
If ProcessUser and ProcessCompany both operate on a single object (that is, the list items, as opposed to the list itself), as the method names seem to imply, then you could make both JSONUser and JSONCompany inherit from an interface, say ICanProcess that has a method Process() that returns a bool. Then you can say:
interface ICanProcess
{
bool Process();
}
class JSONUser : ICanProcess
{
// implement Process for this object
}
static bool GetJSON<T>()
where T: ICanProcess
{
...
bool result = true;
foreach(T it in oJSON)
{
// I assume the result should be AND of all items process result
result = result && it.Process();
}
}
Upvotes: 0