have a look at this code please:
public void BindElements<T>(IEnumerable<T> dataObjects)
{
Paragraph para = new Paragraph();
foreach (T item in dataObjects)
{
InlineUIContainer uiContainer =
this.CreateElementContainer(item.FirstName ????? )
para.Inlines.Add(uiContainer);
}
FlowDocument flowDoc = new FlowDocument(para);
this.Document = flowDoc;
}
When in write in Visual Studio "item.XXX" I should get the properties from my entitiy like .FirstName or .LastName. I do not know wether dataObjects is an IEnumerable or IOrder etc... it must be generic!
How can I get the real properties form item ? Only with Reflection?
Oded is right, it doesn't seem (to him or me) to make any sense to try and make this method generic. You are trying to genericize a method whose functionality is actually specific to a few types.
Now, that said, it seems the bulk of the function is independent of this property you want to access. So why not split it into two parts: that which can be genericized, and that which can't:
Something like this:
void BindElements<T, TProperty>(IEnumerable<T> dataObjects,
Func<T, TProperty> selector)
{
Paragraph para = new Paragraph();
foreach (T item in dataObjects)
{
// Notice: by delegating the only type-specific aspect of this method
// (the property) to (fittingly enough) a delegate, we are able to
// package MOST of the code in a reusable form.
var property = selector(item);
InlineUIContainer uiContainer = this.CreateElementContainer(property)
para.Inlines.Add(uiContainer);
}
FlowDocument flowDoc = new FlowDocument(para);
this.Document = flowDoc;
}
Then your overloads dealing with specific types, e.g., IPerson
, can reuse this code (which I suspect may be what you were after all along—code reuse):
public void BindPeople(IEnumerable<IPerson> people)
{
BindElements(people, p => p.FirstName);
}
...then for IOrder
:
public void BindOrders(IEnumerable<IOrder> orders)
{
BindElements(orders, o => p.OrderNumber);
}
...and so on.
If you add a constraint to the generic type (say it has to implement the IPerson
interface), you can use any methods defined on the interface:
public void BindElements<T>(IEnumerable<T> dataObjects) where T : IPerson
If IPerson
defines FirstName
and LastName
peroperties, you can use them with T
.
See the link for the different types of generic constraints possible.
Adding to Dan's answer, Func<T, TProperty> selector
merely says that selector
is an identifier for a method that takes in a parameter of type T
and has a return type of TProperty
. So, a valid method that could be passed into BindElements
as a second parameter would be, for example,
string CreatePersonElement(IPerson person) {
return string.Format("{0} {1}", person.FirstName, person.LastName);
}
In this case, TProperty
would be a string
and T
would IPerson
. You can then call BindElements
like so
BindElements(myPersonCollection,CreatePersonElement);
where myPersonCollection can just be whatever List<T>
you were referring to. Then moving on to the foreach loop
foreach (T item in dataObjects) {
// Notice: by delegating the only type-specific aspect of this method
// (the property) to (fittingly enough) a delegate, we are able to
// package MOST of the code in a reusable form.
var property = selector(item);
InlineUIContainer uiContainer = this.CreateElementContainer(property)
para.Inlines.Add(uiContainer);
}
property
is being set to an object of type TProperty
, which in the case of CreatePersonElement
is a string
. If a string
doesn't work for you, just change the return type of the method to be whatever CreateElementContainer
is accepting as its parameter.
You would then have one of these methods to pass into the second parameter for BindElements
for each type you want to support (i.e. ICustomer
, IOrder
).
I would read http://msdn.microsoft.com/en-us/library/d5x73970.aspx and think about Oded's answer again.