确定类是否具有特定泛型参数类型的泛型祖先 [英] Decide if class has generic ancestor of specific generic argument type

查看:64
本文介绍了确定类是否具有特定泛型参数类型的泛型祖先的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说我有一个 CoolStorageClass 类,它是从 StorageClassBase 继承的:

Say I have a class CoolStorageClass, which inherits from StorageClassBase:

public abstract class StorageClassBase
{
}

public class CoolStorageClass : StorageClassBase
{
}

然后我有一个通用的抽象 BaseClass< T> .重要的是, T 只能是 StorageClassBase 类型.

Then I have a generic abstract BaseClass<T>. It is important, that T can only be of type StorageClassBase.

public abstract class BaseClass<T> where T : StorageClassBase
{
}

然后我以 CoolClass 的形式使用 T 作为 CoolStorageClass 来实现 BaseClass :/p>

Then I have the implementation of the BaseClass with T as CoolStorageClass in the form of CoolClass:

public class CoolClass : BaseClass<CoolStorageClass>
{
}

我想选择所有实现了 BaseClass< StorageClassBase> 抽象类的对象.

I want to select all of my object, which are implementing the BaseClass<StorageClassBase> abstract class.

  1. 检查 BaseClass 的泛型是否有意义?我的意思是,我可以拥有从 BaseClass< DifferentStorageClassBase> 继承的类...我问这个问题,因为以下链接的答案并不关心泛型类型的泛型参数,仅关心类型本身

  1. does it make sense to check the generic of BaseClass? I mean, I could have classes, which inherit from BaseClass<DifferentStorageClassBase>... I ask this, because the linked answer below does not care about the generic parameter of the generic type, only the type itself.

如何检查 Type 是否实现 BaseClass< StorageClassBase> ?我发现以下 answer ,但是它没有检查通用参数的类型.所以我将其修改为:

how do I check if a Type implements BaseClass<StorageClassBase>? I have found following answer, but it does not check the type of the generic parameter. So I modified it into this:

public static class TypeExtensions
{
    //https://stackoverflow.com/a/457708
    public static bool HasBaseClassOf(this Type t, Type toCheck, Type genericParameter)
    {
        while ((t != null) && (t != typeof(object)))
        {
            var cur = t.IsGenericType ? t.GetGenericTypeDefinition() : t;
            if (toCheck == cur)
            {
                //also check whether the generic types match
                if (t.GenericTypeArguments[0].IsSubclassOf(genericParameter))
                {
                    return true;
                }
            }
            t = t.BaseType;
        }

        return false;
    }
}

但这仅检查一种通用类型,我不明白为什么我必须检查 t.GenericTypeArguments 而不是 cur.GenericTypeArguments .

But this only checks for one generic type, and I don't understand why I have to check t.GenericTypeArguments instead of cur.GenericTypeArguments.

  1. 检查所有通用类型参数和 BaseClass 的正确方法是什么?

当前,我必须像这样调用函数: o.GetType().HasBaseClassOf(typeof(BaseClass< StorageClassBase>),typeof(StorageClassBase)).我应该如何修改该函数以使其能够这样调用: o.GetType().HasBaseClassOf(typeof(BaseClass< StorageClassBase>))?

Currently I have to call the function like this: o.GetType().HasBaseClassOf(typeof(BaseClass<StorageClassBase>), typeof(StorageClassBase)). How should I modify the function to be able to call it like this: o.GetType().HasBaseClassOf(typeof(BaseClass<StorageClassBase>))?


最小的可复制示例:


Minimal reproducible example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MinimalReproducibleExample
{
public abstract class StorageClassBase
{
    //does something
}

public class CoolStorageClass : StorageClassBase
{
}

public abstract class BaseClass<T> where T : StorageClassBase
{
}
public class CoolClass : BaseClass<CoolStorageClass>
{
}

public static class TypeExtensions
{
    //https://stackoverflow.com/a/457708
    public static bool HasBaseClassOf(this Type t, Type toCheck, Type genericParameter)
    {
        while ((t != null) && (t != typeof(object)))
        {
            var cur = t.IsGenericType ? t.GetGenericTypeDefinition() : t;
            if (toCheck == cur)
            {
                //also check whether the generic types match
                if (t.GenericTypeArguments[0].IsSubclassOf(genericParameter))
                {
                    return true;
                }
            }
            t = t.BaseType;
        }

        return false;
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<object> myObjects = new List<object>();
        myObjects.Add(new CoolClass());
        myObjects.Add(new CoolClass());
        myObjects.Add(new object());
        myObjects.Add(new object());

        var t1 = myObjects.Where(o => o.GetType().HasBaseClassOf(typeof(BaseClass<>), typeof(StorageClassBase))).ToList();


        Console.ReadKey();
    }
}
}

推荐答案

不确定您是否清楚,但是 GetGenericTypeDefinition 返回所谓的开放通用类型,即baseClass<>..如果您不关心类型的泛型参数,这将很有用.但这似乎并不能完全满足您的要求.

Not sure if this is clear to you or not, but GetGenericTypeDefinition returns a so called open generic type, i.e. baseClass<>. This can be useful if you do not care about the generic arguments for a type. But that does not seem to do quite what you want to.

为了稍微简化问题陈述,让我们声明一堆我们可以使用的较短类型,简单地讲,我们不必写太多次BaseClass.

To simplify the problem statement a bit, lets declare a bunch of shorter types that we can use, simply so we do not have to write BaseClass so many times.

public class T1{}
public class T2 : T1{}
public class Tx : T1{}

public class U1<T> where T : T1{}
public class U2 : U1<T2>{}
public class U2a : U2{}
public class Ux : U1<T1>{}

如果我正确地理解了您的要求,则要检查对象是否从特定的开放通用类型继承,以及type参数是否可以从其他类型分配.

If I understand your requirements correctly you want to check if a object inherit from a specificed open generic type, and if the type argument is assignable from some other type.

即以下语句应为:

    [Test]
    public void Test()
    {
        Assert.IsTrue(Test<U2>(new U2a()));
        Assert.IsTrue(Test<U1<T1>>(new U2()));
        Assert.IsTrue(Test<U1<T2>>(new U2()));
        Assert.IsTrue(Test<U1<T2>>(new U2a()));
        
        Assert.IsFalse(Test<U1<Tx>>(new U2a()));
        Assert.IsFalse(Test<Ux>(new U2a()));
    }

以下测试应满足以下要求:

The following test should fulfill these statements:

    public bool Test<T>(object obj)
    {
        var tType = typeof(T);
        if (tType.IsGenericType)
        {
            var genericType = tType.GetGenericTypeDefinition();
            var genericArguments = tType.GenericTypeArguments;
            return obj.GetType().BaseTypes().Any(IsMatchingGenericType);
            bool IsMatchingGenericType(Type t)
            {
                if (!t.IsGenericType)
                    return false;
                if (t.GetGenericTypeDefinition() != genericType)
                    return false;
                if (t.GenericTypeArguments.Length != genericArguments.Length)
                {
                    return false;
                }

                for (int i = 0; i < genericArguments.Length; i++)
                {
                    if (!genericArguments[i].IsAssignableFrom(t.GenericTypeArguments[i]))
                    {
                        return false;
                    }
                }
                return true;
            }
        }
        else
        {
            return tType.IsInstanceOfType(obj);
        }
    }

这使用 IsAssignableFrom 而不是 IsSubclassOf ,如果使用接口,则两者之间会有一些差异.

This uses IsAssignableFrom rather than IsSubclassOf of, there is some differences between the two if you are using interfaces.

请注意,以下代码不会编译 U1< T1>.t1 =新的U1< T2>(),因为通用类型不是协变的.为此,您需要声明一个协变接口,如果这样做,则可以使用 IsAssignableFrom 而不是那种笨拙的方法.

Note that the following does not compile U1<T1> t1 = new U1<T2>(), since the generic type is not covariant. For this you would need to declare a covariant interface, and if you do that, you can just is IsAssignableFrom instead of that large cumbersome method.

我也想知道您是否以正确的方式来解决这个问题.通常最好将公共功能放在单独的类中,而不是使用继承.在大多数情况下,您要避免检查类型,而应将逻辑放在类中.如果要使逻辑与类型有关,但仍与类分离,则可以经常使用访客模式.

I also wonder if you are approaching this in the correct way. It is usually better to put common functionality in separate classes rather than using inheritance. In most cases you want to avoid checking types, and let put the logic in the class instead. If you want to make the logic type-dependent but still separate from the classes, the visitor pattern can often be used.

这篇关于确定类是否具有特定泛型参数类型的泛型祖先的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆