当前位置: 首页 > news >正文

.NET/C# 判断某个类是否是泛型类型或泛型接口的子类型

.NET 中提供了很多判断某个类型或实例是某个类的子类或某个接口的实现类的方法,然而这事情一旦牵扯到泛型就没那么省心了。

本文将提供判断泛型接口实现或泛型类型子类的方法。


本文内容

      • .NET 中没有自带的方法
      • 我们需要自己编写方法

.NET 中没有自带的方法

对于实例,.NET 中提供了这些方法来判断:

if (instance is Foo || instance is IFoo)
{
}

对于类型,.NET 中提供了这些方法来判断:

if (typeof(Foo).IsAssignableFrom(type) || typeof(IFoo).IsAssignableFrom(type))
{
}

或者,如果不用判断接口,只判断类型的话:

if (type.IsSubClassOf(typeof(Foo)))
{
}

对于 typeof 关键字,不止可以写 typeof(Foo),还可以写 typeof(Foo<>)。这可以得到泛型版本的 Foo<T> 的类型。

不过,如果你试图拿这个泛型版本的 typeof(Foo<>) 执行上述所有判断,你会发现所有的 if 条件都会是 false

我们需要自己编写方法

typeof(Foo<>)typeof(Foo<SomeClass>) 之间的关系就是 GetGenericTypeDefinition 函数带来的关系。

所以我们可以充分利用这一点完成泛型类型的判断。

比如,我们要判断接口:

public static bool HasImplementedRawGeneric(this Type type, Type generic)
{
    // 遍历类型实现的所有接口,判断是否存在某个接口是泛型,且是参数中指定的原始泛型的实例。
    return type.GetInterfaces().Any(x => generic == (x.IsGenericType ? x.GetGenericTypeDefinition() : x));
}

而如果需要判断类型,那么就需要遍历此类的基类了:

public static bool IsSubClassOfRawGeneric([NotNull] this Type type, [NotNull] Type generic)
{
    if (type == null) throw new ArgumentNullException(nameof(type));
    if (generic == null) throw new ArgumentNullException(nameof(generic));

    while (type != null && type != typeof(object))
    {
        isTheRawGenericType = IsTheRawGenericType(type);
        if (isTheRawGenericType) return true;
        type = type.BaseType;
    }

    return false;

    bool IsTheRawGenericType(Type test)
        => generic == (test.IsGenericType ? test.GetGenericTypeDefinition() : test);
}

于是,我们可以把这两个方法合成一个,用于实现类似 IsAssignableFrom 的效果,不过这回将支持原始接口(也就是 typeof(Foo<>))。

/// <summary>
/// 判断指定的类型 <paramref name="type"/> 是否是指定泛型类型的子类型,或实现了指定泛型接口。
/// </summary>
/// <param name="type">需要测试的类型。</param>
/// <param name="generic">泛型接口类型,传入 typeof(IXxx&lt;&gt;)</param>
/// <returns>如果是泛型接口的子类型,则返回 true,否则返回 false。</returns>
public static bool HasImplementedRawGeneric([NotNull] this Type type, [NotNull] Type generic)
{
    if (type == null) throw new ArgumentNullException(nameof(type));
    if (generic == null) throw new ArgumentNullException(nameof(generic));

    // 测试接口。
    var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
    if (isTheRawGenericType) return true;

    // 测试类型。
    while (type != null && type != typeof(object))
    {
        isTheRawGenericType = IsTheRawGenericType(type);
        if (isTheRawGenericType) return true;
        type = type.BaseType;
    }

    // 没有找到任何匹配的接口或类型。
    return false;

    // 测试某个类型是否是指定的原始接口。
    bool IsTheRawGenericType(Type test)
        => generic == (test.IsGenericType ? test.GetGenericTypeDefinition() : test);
}

相关文章:

  • .NET/C# 使用反射调用含 ref 或 out 参数的方法
  • WPF 多线程 UI:设计一个异步加载 UI 的容器
  • .NET 命令行参数包含应用程序路径吗?
  • 分析现有 WPF / Windows Forms 程序能否顺利迁移到 .NET Core 3.0(使用 .NET Core 3.0 Desktop API Analyzer )
  • C# 空合并操作符(??)不可重载?其实有黑科技可以间接重载!
  • UWP 轻量级样式定义(Lightweight Styling)
  • 预编译框架,开发高性能应用 - 课程 - 微软技术暨生态大会 2018
  • 将 UWP 中 CommandBar 的展开方向改为向下展开
  • .NET 中创建支持集合初始化器的类型
  • .NET 中让 Task 支持带超时的异步等待
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • WPF 的 ElementName 在 ContextMenu 中无法绑定成功?试试使用 x:Reference!
  • WPF 中的 NameScope
  • Windows 下的高 DPI 应用开发(UWP / WPF / Windows Forms / Win32)
  • 技术、产品、交流、思考 - 微软技术暨生态大会 2018
  • JS 中的深拷贝与浅拷贝
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • 345-反转字符串中的元音字母
  • android 一些 utils
  • Java 23种设计模式 之单例模式 7种实现方式
  • JSONP原理
  • Laravel 中的一个后期静态绑定
  • Markdown 语法简单说明
  • ng6--错误信息小结(持续更新)
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • Shadow DOM 内部构造及如何构建独立组件
  • Spring Cloud中负载均衡器概览
  • 回流、重绘及其优化
  • 巧用 TypeScript (一)
  • 一个JAVA程序员成长之路分享
  • 原生Ajax
  • 怎么将电脑中的声音录制成WAV格式
  • 组复制官方翻译九、Group Replication Technical Details
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • $GOPATH/go.mod exists but should not goland
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (7)STL算法之交换赋值
  • (Python) SOAP Web Service (HTTP POST)
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .chm格式文件如何阅读
  • .libPaths()设置包加载目录
  • .NET 反射 Reflect
  • .net 流——流的类型体系简单介绍
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .net访问oracle数据库性能问题
  • .NET上SQLite的连接
  • .Net中的设计模式——Factory Method模式
  • /dev/sda2 is mounted; will not make a filesystem here!
  • @modelattribute注解用postman测试怎么传参_接口测试之问题挖掘