简体   繁体   中英

Generic list/sublist handling

Let's say we have a class

class ComplexCls
{
  public int Fld1;
  public string Fld2;
  //could be more fields
}

class Cls
{
  public int SomeField;
}

and then some code

class ComplexClsList: List<ComplexCls>;
ComplexClsList myComplexList;
// fill myComplexList

// same for Cls    
class ClsList : List<Cls>;
ClsList myClsList;

We want to populate myClsList from myComplexList, something like (pseudocode):

foreach Complexitem in myComplexList
{
  Cls ClsItem = new Cls();
  ClsItem.SomeField = ComplexItem.Fld1;
}

The code to do this is easy and will be put in some method in myClsList. However I'd like to design this as generic as possible, for generic ComplexCls. Note that the exact ComplexCls is known at the moment of using this code, only the algorithm shd be generic.
I know it can be done using (direct) reflection but is there other solution? Let me know if the question is not clear enough. (probably isn't). [EDIT] Basically, what I need is this: having myClsList, I need to specify a DataSource (ComplexClsList) and a field from that DataSource (Fld1) that will be used to populate my SomeField

This is just a mapping, so use some simple LINQ:

ClsList myClsList = new ClsList();
myClsList.AddRange(
  myComplexList.Select(Complexitem => new Cls { SomeField = Complexitem.Fld1 })
);

Okay, the easier version assuming we have a known target field on a class (I've written this as an extension method, no need to do

public IEnumerable<Cls> MapField<TSource>(IEnumerable<TSource> sourceList, 
                                          Func<TSource, int> sourceSelector)
{
  return sourceList.Select(x => new Cls {SomeField = sourceSelector(x)});
}

Called this way

IEnumerable<Cls> result = MapField(myComplexList, x => x.Fld1);

Aside: Since your myComplexList of type ComplexClsList inherits from List (which implements IEnumerable this will work. The result isn't of type ClsList that you wanted, but you could easily call .ToList() on the result and provide a constructor on ClsList that takes a List<Cls> .


And the more complicated version for when we don't know the target field (or type)...

public IEnumerable<TResult> MapField<TSource, TResult, TMap>(
    IEnumerable<TSource> sourceList, 
    Func<TSource, TMap> sourceSelector,
    Func<TMap, TResult> resultCreator)
{
    return sourceList.Select(x => resultCreator(sourceSelector(x)));
}

Not as pretty to call....

IEnumerable<Cls> result = MapField(
    myComplexList,
    source => source.Fld1,
    valueToMap => new Cls() {SomeField = valueToMap});

Might be a better way, but it's not occurring to me at the moment.

Edit: Actually, you could combine the two Func on the last one into a single one that takes a TSource and creates and maps the necessary fields to TResult , but I'm really not sure what you're gaining with that extra layer of abstraction...

You may want to reconsider extending List classes in the first place. What does inheritance give you, in this case? I suspect that you'll be better off favoring composition over inheritance here. One possible approach would be:

// If you would say that a ComplexCls "is a" Cls, then maybe your inheritance
// relationship belongs here instead.
public class ComplexCls : Cls { 
}

public class ClsList
{
  public IReadOnlyCollection<Cls> Items {get;set;}
}

public class ComplexClsList
{
  public IReadOnlyCollection<ComplexCls> Items {get;set;}
}

Then you can create a ClsClist easily.

ClsList basicList = new ClsList{Items = complexList.Items};

But you may want to take it a step farther and question why the ClsList and ComplexClsList classes exist at all. Why not simply pass around List s directly. I mean, what's the difference between a ClsList and a "List of Clses" ( List<Cls> )?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM