I have this extension method for my BusinessObject class:
public static class Extensions
{
public static T Clone<T>(this T obj)
where T: BusinessObject<T>, new()
{
T newObj = new T();
var props = newObj.Properties;
foreach (var p in props)
newObj.Properties[p.Name].SetValue(newObj, obj.Properties[p.Name].GetValue(obj));
return newObj;
}
}
The contents of that method work great, but I have a non-generic BusinessObject class and a generic counterpart. A lot of my code passes around an instance of a generic version of the object inside a non-generic variable (please don’t ask me 20 questions about why). I haven’t had a problem until now because when I call this extension method it expects a generic version.
The code that calls the Clone method uses a BusinessObject variable that contains an instance of a BusinessObject variable. How can I cast the variable to what it actually is? In other words:
Customer cust = new Customer(); // Customer derives from BusinessObject<Customer>
var CustomerClone = cust.Clone(); // This works
BusinessObject obj = cust;
var clone = obj.Clone(); // This doesn't work
Now of course in this example I know that ‘obj’ is a Customer, but in my actual method I don’t know that as it could be any number of derived types. I can find out what the derived type is easily enough by simply using GetType(), but how do I cast the variable to that type at runtime?
In your case, I would say “lose the
T” – you aren’t using for anything important that can’t be done in other ways (Activator.CreateInstance, for example). You could offer two APIs – one generic, one non-generic, to allow for convenient casting. For example:It is also quite key because the
Tyou want is not actually the declarationT, but the concreteT. Meaning: if you have aSuperCustomer : Customer, but place it in aCustomervariable, and then callClone, you want to get a newSuperCustomer.T, however, would beCustomer. UsingGetType()would be far more reliable.Another useful trick here is
dynamic, which is a sneaky way of flipping from non-generic to generic. Consider:will write
System.Int32. It has flipped to the correctTon the fly, despite only really knowing aboutobjectat the compiler (dynamicis implemented mostly asobject, with some fancy bits).However, to emphasise – I wouldn’t use that here: I would just have:
with
dynamicas my fallback strategy if I really needed the generics.