tags:

views:

82

answers:

6

Assume some domain and view objects (that have no common base class)

public 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 :)

+2  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

Kev Hunter
+2  A: 

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));
Lee
Thanks that should be exactly what I need.
Jedidja
Actually could I make the Func<U,T> implicit? Given that it's always called Create? I'd rather not have to pass it in every time.
Jedidja
+2  A: 

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));
Matt Dearing
+1  A: 

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; } 
} 
Fiona Holder
I'd rather not mess with the class model.
Jedidja
You do get some advantages though, in case they ever have more 'custom' functionality. Or if you ever want to create a list of views, for example.
Fiona Holder
@Jedidja It sounds like your trying to use a generic-programming hammer where an object-oriented-programming ratchet would be better suited. You have conceptually related to items; have both your Domain classes implement interface IViewable<T> { T CreateView(); } and move on.
Robert Davis
There's no way the Domain can ever know about the View. The Domain is defined in its own stand-alone assembly. And I'd rather not have the View know about the Domain either. That's why I wanted a third party to take of doing the conversion :)
Jedidja
I realise I didn't mention that in the original question; would have definitely given for a different response.
Jedidja
+1  A: 

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.

kvb
A: 

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.

Robert Davis