How can I create a generic method to convert lists of objects when conversion methods for single objects exist?
Assume some domain and view objects (that have no common base class)
开发者_如何学JAVApublic class DomainA
{
public int MyProperty { get; set; }
}
public class ViewA
{
public int MyProperty { get; set; }
}
public class DomainB
{
public string MyProperty { get; set; }
}
public class ViewB
{
public string MyProperty { get; set; }
}
and a class that can convert from domain to corresponding view
public class ViewFactory
{
public static ViewA Create(DomainA value)
{
return new ViewA() { MyProperty = value.MyProperty };
}
public static ViewB Create(DomainB value)
{
return new ViewB() { MyProperty = value.MyProperty };
}
}
Is it possible to write a method along the lines of
public static List<T> Create<T, U>(IEnumerable<U> values)
{
return new List<T>(from v in values where v != null select Create((U)v));
}
that can convert lists of the "domain" objects to lists of the "view" objects given that there are already methods for converting a single object?
I feel like I'm missing something really stupid and basic about generics asking this question..better to learn the answer than remain clueless though :)
Yes if you modify your Create function to take a mapping between domain and views:
public static List<T> Create<T, U>(IEnumerable<U> values, Func<U, T> mapFunc)
{
return values.Select(v => mapFunc(v)).ToList();
}
You can then do the mapping using your ViewFactory methods:
IEnumerable<DomainA> domAs = //whatever
var aViews = Create(domAs, a => ViewFactory.Create(a));
you should be able to use the ConvertAll and ToList Functions
ConvertAll allows you to project your changes which would return an IEnumerable of T in your case and then a ToList would convert from the IEnumerable to the List
So Stolen from the MSDN article
List<PointF> lpf = new List<PointF>();
lpf.Add(new PointF(27.8F, 32.62F));
lpf.Add(new PointF(99.3F, 147.273F));
lpf.Add(new PointF(7.5F, 1412.2F));
Console.WriteLine();
foreach( PointF p in lpf )
{
Console.WriteLine(p);
}
List<Point> lp = lpf.ConvertAll(
new Converter<PointF, Point>(PointFToPoint));
Console.WriteLine();
foreach( Point p in lp )
{
Console.WriteLine(p);
}
you should be able to go from U to T as they have gone from PointF to Point
you may not even need the ToList
You shouldn't need a custom method to create the lists. You can just do the following:
List<DomainA> domains = GetDomainList();
List<ViewA> views = domains.ConvertAll<ViewA>(x => ViewFactory.Create(x));
Hmm, it looks to me like you need a more object-oriented model rather than generics:
public abstract class Domain
{
public abstract View CreateView();
}
public abstract class View
{
}
public class DomainA : Domain
{
public int MyProperty { get; set; }
public override View CreateView()
{
return new ViewA() { MyProperty = value.MyProperty };
}
}
public class ViewA : View
{
public int MyProperty { get; set; }
}
public class DomainB : Domain
{
public string MyProperty { get; set; }
public override View CreateView()
{
return new ViewB() { MyProperty = value.MyProperty };
}
}
public class ViewB : View
{
public string MyProperty { get; set; }
}
You can't create a generic method like you are attempting to do. In your example, there are only a few ways to set T
and U
which would work (e.g. Create<ViewA,DomainA>(domains)
would be meaningful, but Create<string,int>(ints)
wouldn't). The caller of a generic method decides at which types to call it, so generic methods must work for any type parameters, which your method does not.
Others have given several good suggestions for how to achieve the result you want.
Ultimately it looks like you want is a little thing called multiple dispatch. Which unfortunately for all of us is not implemented in C# 3. If you want to have your program automagically figure out which method to call, you're going to have to either use reflection or a virtual function using single dispatch.
interface IViewFactory<TView, TDomain>
{
TView Create(TDomain domain);
}
class ViewFactoryA : IViewFactory<ViewA, DomainA>
{
...
}
class ViewFactoryB : IViewFactory<ViewB, DomainB>
{
...
}
now your initialization code just needs to create the correct factory object.
精彩评论