In a service (that I cannot alter) I have two object classes Bar and Baz with mostly similar properties (but sadly NO, they don't derive from the same base class or inherit from the same Interface... yeah -- dumb), as well as a dependency class related to their relative BarQux and BazQux properties:
public class Bar public class Baz
{ {
public int ID { get; set; } public int ID { get; set; }
public bool Active { get; set; } public bool Active { get; set; }
public int BarQux { get; set; } public int BazQux { get; set; }
... ...
} }
public class Qux
{
public int ID { get; set; } // Corresponds to BarQux and BazQux
public string Name { get; set; }
}
In a WPF screen, I am binding a list of each type (Baz and Bar) to two seperate ListViews. I need each to have an additional Selected CheckBox column. To do so, I've created a wrapper class with the common properties, the additional Selected property, and a constructor for each:
public class Foo
{
public Foo(Bar bar, Qux qux)
{
this.Active = bar.Active;
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
...
}
public Foo(Baz baz, Qux qux)
{
this.Active = baz.Active;
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
...
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
...
}
To convert each collection of class Baz and Bar to collections of Foo, I created the following extension methods:
public static List<Foo> ToFoo(this IEnumerable<Bar> bars, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (Bar bar in bars)
{
Foo foo = new Foo(bar, quxs.Single(qux => qux.ID == bar.BarQux));
foos.Add(foo);
}
return foos;
}
public static List<Foo> ToFoo(this IEnumerable<Baz> bazs, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (Baz baz in bazs)
{
Foo foo = new Foo(baz, quxs.Single(qux => qux.ID == baz.BazQux));
foos.Add(foo);
}
return foos;
}
Question:
How do I make this generic?
Theory, Implementation, and Error:
Since the constructors are virtually the same besides the Bar and Baz parameters, can I somehow use generic type T to make one constructor and still grab the properties?
public class Foo<T>
{
public Foo(T obj, Qux qux)
{
this.Active = obj.Active; // 'T' does not contain a definition for 'Active'...
this.Qux = string.Format("{0} - {1}", qux.ID, qux.Name);
...
}
...
}
Change the constructors to receive the whole collection of Qux objects and do the quxs.Single(qux => qux.ID == object.ObjectQux) logic there. Then make the extension methods into one generic method, something like the following.
public static List<Foo> ToFoo<T>(this IEnumerable<T> objCollection, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (T obj in objCollection)
{
Foo foo = new Foo(obj, quxs); // The best overloaded method... has some invalid arguments.
foos.Add(foo);
}
return foos;
}
Both 1 and 2 combined? Anything I haven't thought of?
If you have limited properties and the number of items in the list is also few then you can use Reflection. Since you will use this in WPF I would also suggest that the process be moved to a separate background thread.
Generic Foo
public class Foo<T>
{
public Foo(T obj, Qux qux)
{
//this.Active = obj.Active;
var fooProps = GetType().GetProperties().ToList();
var tProps = typeof(T).GetProperties()
.Where(p =>
{
var w = fooProps.FirstOrDefault(f => f.Name == p.Name);
return w != null;
}).ToList();
foreach (var propertyInfo in tProps)
{
var val = propertyInfo.GetValue(obj);
fooProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val);
}
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
}
Your extension methods
public static IEnumerable<Foo<Bar>> ToFoo(this IEnumerable<Bar> bars, IEnumerable<Qux> quxs)
{
return bars.
Select(bar => new Foo<Bar>(bar, quxs.Single(qux => qux.ID == bar.BarQux))).ToList();
}
public static IEnumerable<Foo<Baz>> ToFoo(this IEnumerable<Baz> bazs, IEnumerable<Qux> quxs)
{
return bazs.
Select(baz => new Foo<Baz>(baz, quxs.Single(qux => qux.ID == baz.BazQux))).ToList();
}
public static IEnumerable<Qux> ToQuxes(this IEnumerable<BazQux> bazQuxs)
{
return bazQuxs.Select(b => new Qux(typeof(BazQux), b)).ToList();
}
public static IEnumerable<Qux> ToQuxes(this IEnumerable<BarQux> barQuxes )
{
return barQuxes.Select(b => new Qux(typeof(BarQux), b)).ToList();
}
Similarly you can also convert your BarQux or BazQux into the non generic Qux class.
public class Qux
{
public int ID { get; set; } // Corresponds to BarQux and BazQux
public string Name { get; set; }
public Qux(Type type, object obj)
{
var ob = Convert.ChangeType(obj, type);
var quxProps = GetType().GetProperties();
var obProps = ob.GetType().GetProperties()
.Where(p =>
{
var w = quxProps.FirstOrDefault(f => f.Name == p.Name);
return w != null;
}).ToList();
foreach (var propertyInfo in obProps)
{
var val = propertyInfo.GetValue(obj);
quxProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val);
}
}
}
You can then just call ToFoo extension method and voila you have a list of Foo.
You can also convert Foo to non-generic by using the logic of Qux class
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With