Reputation: 97
I'm trying to create a generic Type Converter. All types will have a base-class of Entity
, and an interface of IConvertable
. I've implemented an interface for objects that can be converted:
public interface IConvertable
{
public bool CanConvert<TTarget>() where TTarget : Entity, IConvertable
Tuple<bool, Entity> TryConvertTo<TTarget> where TTarget: Entity, IConvertable
}
The idea is that each specific sub-class of Entity (partial
classes, auto-generated code, I cannot directly modify) will have a CanConvert<TTarget>
, which will determine if it can be converted to the target type.
I was trying to put together the generic type converter, which will take IEnumerable
s of Entity
, transforming them (And performing other tasks at the same time - for instance through other interfaces, reparenting), and then returning the transformed Entity
classes.
My initial realisation was that, effectively, CanConvert<TTarget>
is static
, in that it is always constant from a TSource
to a TTarget
, so when I build my converter:
public class EntityConverter<TSource, TTarget> where TSource : Entity, IConvertable
where TTarget : Entity, IConvertable
{
private IEnumerable<TSource> Records { get; set; }
public EntityConverter(IEnumerable<TSource> records) {
if (!TSource.CanConvert<TTarget>()) {
// error condition, reporting, etc.
}
Add(records);
}
public void Add(IEnumerable<TSource> records) { ... }
public void Convert() {
// massively simplified for example - there will actually be a store
// holding, more functionality, parent-child relationships, but
// this is the basic principle
for (var c in convertables) {
c.TryConvertTo<TTarget>();
}
}
}
Obviously, there are many reasons why static
functions cannot be used on generics (Eric Lipperts excellent descriptions of this are available on his blog), however this leaves me in a quandry - how do I define something that describes the type (Not instance) and can be referenced from the type, in a generic structure, without having massive external partial
constructs or evil factories - this is supposed to be an extinsible, re-usable structure than someone can call from another class by simply extending the partial
types with the interface implementation.
I suspect I'm viewing things the wrong way around, but I can't see what. How can I determine if a type conversion is valid, without having something else in the middle to hold the type conversions?
edit
Have updated the code sample a bit (Previously had the conversion in the constructor). I actually do the conversion quite a bit later on in the process, as I have multiple sources of convertables which need to be added, prior to the actual conversion. This results in the actual error (If !CanConvert<TTarget>()
) coming quite far down the line. For each instantiation of this class, it will be converting from type TSource
to TTarget
, so the validity of that conversion should be known in the constructor, and it should error in the constructor, rather than during the conversion later on... At least, that's my read on things.
Thanks to @Dennis for clarifying some of my thoughts on things... I may end up reconstructing this anyway, but I don't feel I'm far off. I have thought about putting the CanConvert<TTarget>()
check inside the Add()
function... which feels like I'm nearly there...
Upvotes: 0
Views: 77
Reputation: 64923
From my point of view, it seems like a task for AutoMapper. You want to set properties from an object to other one. That's all.
I would avoid implementing this myself since I doubt you would get what AutoMapper already does in few days or months.
While I understand that this doesn't answer your question at all, I feel that it's all about finding a good solution rather than over-engineering yours.
Upvotes: 1
Reputation: 37770
It's too complicated.
There will be enough to keep TryConvertTo
in IConvertable
.
Just return false
, when target type isn't applicable. Yes, this will be the same for all instances, but what's wrong here?
Note, that you're breaking well-known pattern for Try...
methods, when returning a tuple. IMO, this will be more familiar:
public interface IConvertable
{
bool TryConvertTo<TTarget>(out TTarget entity)
where TTarget: Entity, IConvertable
}
Upvotes: 1