Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
64 views
in Technique[技术] by (71.8m points)

c# - How to turn two methods to one generic?

How can I make a single generic method from the 2 methods ConvertToDictionary that can handle both collections?

EDIT: I rewrote the code, so it can be run as it is.

DgDictHosp differs from DgDictOper in type of property Data IOrderedEnumerable<Hospitalization> vs IOrderedEnumerable<Operation> where Hospitalization and Operation are classes generated by Entity Framework.

using System;
using System.Collections.Generic;
using System.Linq;
    public class DictItem
    {
        public long Id { get; set; }
        public List<string> List { get; set; }
    }
    public class Hospitalization
    {
        public int ID { get; set; }
        public long IDHospitalization { get; set; }
        public int IDDiagnosis { get; set; }
    }
    public class Operation
    {
        public int ID { get; set; }
        public long IDOperation { get; set; }
        public int IDDiagnosis { get; set; }
    }
    public class DgDictOper
    {
        public long Key { get; set; }
        public IOrderedEnumerable<Operation> Data { get; set; }
        public List<string> List { get; set; }
    }
    public class DgDictHosp
    {
        public long Key { get; set; }
        public IOrderedEnumerable<Hospitalization> Data { get; set; }
        public List<string> List { get; set; }
    }
    public class Program
    {
        public static string ToLiteral(int dgid)
        {
            return "dictionary phrase for " + dgid;
        }
        public static Dictionary<long, DictItem> ConvertToDictionary(List<DgDictHosp> list_g)
        {
            var res_dict = list_g.Select(q => new DictItem
            {
                Id = q.Key,
                List = q.Data.Select(t => ToLiteral(t.IDDiagnosis)).ToList()
            }).ToDictionary(k => k.Id);
            return res_dict;
        }
        public static Dictionary<long, DictItem> ConvertToDictionary(List<DgDictOper> list_g)
        {
            var res_dict = list_g.Select(q => new DictItem
            {
                Id = q.Key,
                List = q.Data.Select(t => ToLiteral(t.IDDiagnosis)).ToList()
            }).ToDictionary(k => k.Id);
            return res_dict;
        }
        public static List<Hospitalization> InitHospList()
        {
            List<Hospitalization> list = new List<Hospitalization>();
            list.Add(new Hospitalization() { ID = 1, IDHospitalization = 11, IDDiagnosis = 10 });
            list.Add(new Hospitalization() { ID = 2, IDHospitalization = 11, IDDiagnosis = 20 });
            return list;            
        }
        public static List<Operation> InitOperList()
        {
            List<Operation> list = new List<Operation>();
            list.Add(new Operation() { ID = 1, IDOperation = 22, IDDiagnosis = 30 });
            list.Add(new Operation() { ID = 2, IDOperation = 22, IDDiagnosis = 40 });
            return list;
        }
        public static Dictionary<long, DictItem> GetHospDict(List<Hospitalization> list)
        {
            var res_g =
                (from dg in list
                 group dg by dg.IDHospitalization
                 into dg_group
                 select new DgDictHosp
                 {
                     Key = dg_group.Key,
                     Data = dg_group.OrderBy(g => g.ID)
                 }).ToList();
            var res = ConvertToDictionary(res_g);
            return res;
        }
        public static Dictionary<long, DictItem> GetOperDict(List<Operation> list)
        {
            var res_g =
                (from dg in list
                 group dg by dg.IDOperation
                 into dg_group
                 select new DgDictOper
                 {
                     Key = dg_group.Key,
                     Data = dg_group.OrderBy(g => g.ID)
                 }).ToList();
            var res = ConvertToDictionary(res_g);
            return res;
        }
        public void Main()
        {                        
            foreach (var x in GetHospDict(InitHospList())) 
            {
                Console.WriteLine("Hosp ID: " + x.Key);
                foreach (var y in x.Value.List)
                {
                    Console.WriteLine("Diagosis: " + y);  ;
                }
            }
            foreach (var x in GetOperDict(InitOperList()))
            {
                Console.WriteLine("Operation ID: " + x.Key);
                foreach (var y in x.Value.List)
                {
                    Console.WriteLine("Diagosis: " + y); ;
                }
            }
        }
    }

question from:https://stackoverflow.com/questions/65936217/how-to-turn-two-methods-to-one-generic

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Basically your data structures differ in a "generic" parameter. So you would need a generic interface to accomodate for this, or you could use a generic class and get rid of DgDictHosp and DgDictOper altogether because the generic class could account for all properties:

public class DgDictBaseClass<T> where T : HospOpBase
{
    public long Key { get; set; }
    public IOrderedEnumerable<T> Data { get; set; }
    public List<string> List { get; set; }
}

I restricted the generic parameter to a baseclass that I introduced to simplify the code of the classes Hospitalization and Operation:

public abstract class HospOpBase
{
    public int ID { get; set; }
    public int IDDiagnosis { get; set; }
}

public class Hospitalization : HospOpBase
{
    public long IDHospitalization { get; set; }
}
public class Operation : HospOpBase
{
    public long IDOperation { get; set; }
}

With these tools in your backback you can now start to make your mapping method generic to accomodate for both types of collections. As a parameter you would now pass a List<DgDictBaseClass<T>> list_g and you will return Dictionary<long, DictItem>:

public static Dictionary<long, DictItem> ConvertToDictionary<T>(List<DgDictBaseClass<T>> list_g) where T : HospOpBase
{
    var res_dict = list_g.Select(q => new DictItem
    {
        Id = q.Key,
        List = q.Data.Select(t => ToLiteral(t.IDDiagnosis)).ToList()

    }).ToDictionary(k => k.Id);
    return res_dict;
}

Here again it is usefull to restrict the generic parameter T to be of type HospOpBase. This way the compiler will know that it has the property IDDiagnosis and you can use it in your linq statement.

Now there is only 1 last change to be made to your current code. Since I got rid of the classes DgDictOper and DgDictHosp entirely you need to exchange them with the new type in the methods: GetHospDict and GetOperDict in the select statement :

public static Dictionary<long, DictItem> GetHospDict(List<Hospitalization> list)
{
    var res_g =
        (from dg in list
         group dg by dg.IDHospitalization
         into dg_group
         select new DgDictBaseClass<Hospitalization> // <= change here
         {
             Key = dg_group.Key,
             Data = dg_group.OrderBy(g => g.ID)
         }).ToList();
    var res = ConvertToDictionary(res_g);
    return res;
}

public static Dictionary<long, DictItem> GetOperDict(List<Operation> list)
{
    var res_g =
        (from dg in list
         group dg by dg.IDOperation
         into dg_group
         select new DgDictBaseClass<Operation> // <= change here
         {
             Key = dg_group.Key,
             Data = dg_group.OrderBy(g => g.ID)
         }).ToList();
    var res = ConvertToDictionary(res_g);
    return res;
}

EDIT:

actually there is a way to combine the last 2 mapping methods into 1. The tricky part here is the grouping variable which is actually the only difference in the two methods. You can provide this method as a method parameter. The type is either Func<Operation, long> or Func<Hospitalization, long>. (Which basically means that it is a method that takes an Operation as input parameter and returns a long, in your case the property: dg.IDOperation). So since the 2 classes have now a common base class we can make the method generic, the grouping function parameter of type: Func<T, long> and restrict the generic parameter to be only of type HospOpBase. Now the function would look like this:

public static Dictionary<long, DictItem> GetOperDictNew<T>(List<T> list, Func<T, long> groupingFilter)  where T : HospOpBase
{
    var res_g =
        (from dg in list
         group dg by groupingFilter(dg) // <= use here the grouping filter
         into dg_group
         select new DgDictBaseClass<T>
         {
             Key = dg_group.Key,
             Data = dg_group.OrderBy(g => g.ID)
         }).ToList();
    var res = ConvertToDictionary(res_g);
    return res;
}

Now you can call the new method in both loops:

foreach (var x in GetOperDictNew(InitHospList(), x => x.IDHospitalization))
{
    Console.WriteLine("Hosp ID: " + x.Key);
    foreach (var y in x.Value.List)
    {
        Console.WriteLine("Diagosis: " + y); ;
    }
}
foreach (var x in GetOperDictNew(InitOperList(), x => x.IDOperation))
{
    Console.WriteLine("Operation ID: " + x.Key);
    foreach (var y in x.Value.List)
    {
        Console.WriteLine("Diagosis: " + y); ;
    }
}

and you will get the same output:

Hosp ID: 11
Diagosis: dictionary phrase for 10
Diagosis: dictionary phrase for 20
Operation ID: 22
Diagosis: dictionary phrase for 30
Diagosis: dictionary phrase for 40


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...