General method where T implements the <T> interface

I am trying to create a general data retrieval process. That I am currently working, but there is a part of it that does not seem to be correct, and I hope that there is a better way to accomplish it.

So the idea is that I have classes for each table in the database, here is an example of a class:

public class CMCGRGRGROUP : IFacetsObject<CMCGRGRGROUP> { public int GRGR_CK { get; set; } public string GRGR_NAME { get; set; } public string GRGR_ADDR1 { get; set; } public IEnumerable<CMCGRGRGROUP> ToObject(DataTable table) { return table.AsEnumerable().Select(row => { return new CMCGRGRGROUP { GRGR_CK = Convert.ToInt32(row["GRGR_CK"]), GRGR_NAME = row["GRGR_NAME"].ToString(), GRGR_ADDR1 = row["GRGR_ADDR1"].ToString() }; }); } } 

You will notice that the class implements an interface of its type. The interface simply defines a method called ToObject , which is used to convert the data into a class of this particular type:

 public interface IFacetsObject<T> { IEnumerable<T> ToObject(DataTable obj); } 

Now, here is the method I use to execute the request:

 public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj) where T : new() { using (var conn = new AseConnection(_conn)) { conn.Open(); var cmd = new AseCommand(sql, conn); var dt = new DataTable(); var da = new AseDataAdapter(sql, conn); da.Fill(dt); return obj.ToObject(dt); //this is the interface method } } 

So, the main question: How does the general method know that T must implement IFacetsObject<T> ? This way, I do not need to pass IFacetsObject<T> as a parameter. Ideally, I can change the return line to be something like this:

 return T.ToObject(dt); 

And name it as follows:

 var result = ExecuteQuery<CMCGRGRGROUP>(sql).Take(5); 

Instead of this:

 var result = ExecuteQuery<CMCGRGRGROUP>(sql, new CMCGRGRGROUP()).Take(5); 

I admit that I'm not very familiar with generics, but maybe something in the implementation is wrong.

+7
generics inheritance c # interface
source share
2 answers

You can add a constraint to your ExecuteQuery method. You already have one: T requirement to be new. You would declare it as:

 public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj) where T : IFacetsObject<T>, new() { using (var conn = new AseConnection(_conn)) { conn.Open(); var cmd = new AseCommand(sql, conn); var dt = new DataTable(); var da = new AseDataAdapter(sql, conn); da.Fill(dt); return obj.ToObject(dt); //this is the interface method } } 

So now it’s known that T is an IFacetsObject<T> . Now you can:

 public IEnumerable<T> ExecuteQuery<T>(string sql) where T : IFacetsObject<T>, new() { using (var conn = new AseConnection(_conn)) { conn.Open(); var cmd = new AseCommand(sql, conn); var dt = new DataTable(); var da = new AseDataAdapter(sql, conn); da.Fill(dt); return new T().ToObject(dt); //this is the interface method } } 

Which IMO is still pretty ugly.

EDIT Answer:

Note that you cannot call T.ToObject - the interface cannot define a static method. A workaround is to use the new one to create a new instance of T and call the instance method.

+3
source share

Your interface requires a general restriction. Declare it as follows:

 public interface IFacetsObject<T> where T : IFacetsObject<T> { IEnumerable<T> ToObject(DataTable obj); } 

For this to work, you also need to modify your ad as follows:

 public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj) where T : IFacetsObject<T>, new() 
0
source share

All Articles