开发者

Generic type of a child class doesn't allow parents

开发者 https://www.devze.com 2023-01-17 01:08 出处:网络
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

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.)

0

精彩评论

暂无评论...
验证码 换一张
取 消