Generic type of a child class doesn't allow parents
Given a structure like this:
class Parent { }
class Child : Parent { }
I have a method that takes a generic type with a constraint that the object is of type Child
static void doSomething<T>() where T : Child
{
if (typeof(T) == typeof(Parent))
{
/* ... */
}
else if (typeof(T) == typeof(Child))
{
/* ... */
}
}
the only problem is that, if I have:
class implementsParent : Parent { }
class implementsChild : Child { }
calling the generic method with a type of implementsParent won't work:
doSomething<implementsParent>(); // compile error
doSomething<implementsChild>(); // works fine
I'm trying to work around the fact that overloading a generic method doesn't take into account constraints.
What can I do here?
@Sander Rijken: Additional information.
I need to use the generic type to call an ORM framework method like so:
static void doSomething<T>() where T : Child
{
if (typeof(T) == typeof(Parent))
{
Parent obj = orm.GetObject<T>(criteria);
}
else if (typeof(T) == typeof(Child))
{
Child obj = orm.GetObject<T>(criteria);
}
}
having the constraint be where T : Parent causes the Child obj = orm.GetObject() to break because T cannot be converted to Type 'Child'
@Richard Hein:
Originally, I had 2 methods, each with a constraint to one of child/parent (in this case: XPObject and XPCustomObject from the DevExpress ORM - XPObject inherits from XPCustomObject).
The methods look like so:
static void removeBlank<T>(UnitOfWork uow) where T : XPObject
{
T blank = uow.GetObjectByKey<T>(0):
if (blank != null)
{
blank.Delete();
uow.CommitChanges();
}
}
XPCustomObject is used (in this case) to have PKs of type short (instead of the default int on XPObjects). So 开发者_运维问答the only change is in the call to get the object:
static void removeBlankCustomObject<T>(UnitOfWork uow) where T : XPCustomObject
{
T blank = uow.GetObjectByKey<T>((short)0);
if (blank != null)
{
blank.Delete();
uow.CommitChanges();
}
}
The difference is minimal, so I want to merge the two methods together.
doSomething<implementsParent>();
This fails because it doesn't meet the type constraint. T
isn't derived from Child
Did you mean to declare it:
static void doSomething<T>() where T : Parent
Edit: This will work, given the requirement you added.
static void doSomething<T>() where T : Parent
{
if (typeof(T) == typeof(Parent))
{
T obj = orm.GetObject<T>(criteria);
}
else if (typeof(T) == typeof(Child))
{
T obj = orm.GetObject<T>(criteria);
}
}
Aside from the fact that T doesn't derive from child, I think you'd want to use
if (typeof(T).IsAssignableFrom(typeof(Parent))
...
Otherwise it'll only fire for things that are exactly the type Parent, not a derived type. Also, I don't think == is properly overloaded for Type. I think you need to use .Equals()
Note: I may have gotten the IsAssignableFrom backwards.
This part of your code doesn’t make sense:
static void doSomething<T>() where T : Child
// ^^^^^^^^^^^^^^^ This says that T must be a Child,
// which necessarily means it is not
// going to be a Parent
{
if (typeof(T) == typeof(Parent)) // <-- Therefore, this will never hit
{
You definitely need to define the constraint as T : Parent
if you want to be able to pass in a Parent
.
You said that your problem is that you can’t cast the output of orm.GetObject<T>
to Child
. You are right, you can’t cast it directly — but you can cast it to Parent
first and then to Child
. I think this should work:
static void doSomething<T>() where T : Parent
{
if (typeof(T) == typeof(Parent))
{
Parent obj = (Parent) orm.GetObject<T>(criteria);
}
else if (typeof(T) == typeof(Child))
{
Child obj = (Child) (Parent) orm.GetObject<T>(criteria);
}
}
From the point of view of the compiler, T
is like a member of the class hierarchy, a bit like this:
┌──────────┐
| Parent |
└─┬──────┬─┘
│ │
↓ ↓
┌─────────┐ ┌───────┐
| Child | | T |
└─────────┘ └───────┘
I think this shows you why you can’t cast directly from T
to Child
, but you can upcast to Parent
(which always succeeds because T
is a Parent
) and then downcast to Child
(which does the runtime check for whether it really is a Child
, which of course in your case it will be).
(By the way, I am assuming that you need to use orm.GetObject<T>
and you can’t use orm.GetObject<Child>
. If you can, that would make it simpler, but maybe you can’t because the criteria
depend on T
.)
精彩评论