0

0

.NET的TypeDelegator类的作用是什么?如何包装类型?

幻夢星雲

幻夢星雲

发布时间:2025-09-14 09:24:02

|

851人浏览过

|

来源于php中文网

原创

typedelegator 是 .net 中用于创建可自定义 type 视图的代理类,它通过继承 typedelegator 并重写其 virtual 方法来改变反射行为,而无需修改原始类型;由于 system.type 是 sealed 类,无法直接继承,因此 typedelegator 提供了官方推荐的扩展方式,允许在反射层面拦截和修改类型信息,如修改类型名称或过滤方法;其典型应用场景包括动态代理、aop、orm 延迟加载、元数据注入及序列化框架等高级场景;使用时需继承 typedelegator,在构造函数中传入被包装类型,并重写如 name、getmethods 等方法以定制行为,但需注意它仅改变反射视图,不改变实际对象类型,通过 activator.createinstance 创建的实例仍为原始类型;因此 typedelegator 主要用于操控反射查询结果,为动态生成类型提供统一的 type 接口,常配合 reflection.emit 等技术实现完整代理功能。

.NET的TypeDelegator类的作用是什么?如何包装类型?

.NET
中的
TypeDelegator
类,本质上是一个类型包装器或者说代理。它的核心作用是让你能够基于一个已存在的
Type
对象,创建一个新的、行为上可以被定制或修改的
Type
视图,而无需直接继承或改变原始类型。这在很多场景下非常有用,比如当你需要动态地修改一个类型的反射行为,或者为现有类型添加一些“虚拟”的成员,而又不能直接修改其定义时。

解决方案

TypeDelegator
的设计哲学就是“委托”。它继承自抽象基类
Type
,但其内部持有一个实际的
Type
实例(我们称之为被包装的类型)。
TypeDelegator
的绝大多数成员方法(如
GetMethods
GetProperties
Name
等)都被重写了,它们默认的行为就是简单地调用其内部被包装类型对应的成员。

这意味着,如果你只是实例化一个

TypeDelegator
并传入一个
Type
,它的行为和原始
Type
几乎一模一样。但关键在于,
TypeDelegator
的这些成员方法都是
virtual
的,这为我们提供了重写的机会。通过继承
TypeDelegator
并重写特定的方法,我们就可以在反射系统查询类型信息时,拦截并修改这些查询的结果。

举个例子,你可以重写

GetMethods()
方法,在返回原始类型的方法列表之前,动态地添加一个“不存在”的方法信息,或者过滤掉一些你不想暴露的方法。这就像给一个类型戴上了一副“墨镜”,让外部世界看到的是一个经过你定制的、略有不同的它。

为什么我不能直接继承
System.Type

这是个很好的问题,也是

TypeDelegator
存在的根本原因之一。如果你尝试去继承
System.Type
,你会发现编译器会报错,因为它是一个
sealed
(密封)类。也就是说,微软的设计者们明确禁止了直接从
Type
类派生新的类型。

我个人猜测,这背后可能有几层考虑:首先,

System.Type
是.NET类型系统的核心基石,它的行为需要高度一致性和可预测性。如果允许任意继承,可能会导致各种复杂的、不可预期的行为模式,从而破坏反射机制的健壮性。其次,
Type
的内部实现可能与CLR(Common Language Runtime)的底层运行时紧密耦合,直接继承并修改其行为可能会带来安全或性能上的风险。

因此,当我们需要一个“看起来像某个类型,但又有点不一样”的类型描述时,

TypeDelegator
就成了唯一的、官方推荐的途径。它提供了一个受控的扩展点,而不是一个完全开放的继承模型。这有点像在说:“你可以定制这个类型在反射层面的表现,但你不能改变它作为CLR类型本身的本质。”

魔法映像企业网站管理系统
魔法映像企业网站管理系统

技术上面应用了三层结构,AJAX框架,URL重写等基础的开发。并用了动软的代码生成器及数据访问类,加进了一些自己用到的小功能,算是整理了一些自己的操作类。系统设计上面说不出用什么模式,大体设计是后台分两级分类,设置好一级之后,再设置二级并选择栏目类型,如内容,列表,上传文件,新窗口等。这样就可以生成无限多个二级分类,也就是网站栏目。对于扩展性来说,如果有新的需求可以直接加一个栏目类型并新加功能操作

下载

TypeDelegator
在实际中有什么用武之地?

说实话,

TypeDelegator
不是我们日常写业务代码会频繁接触的类,它更多地出现在一些高级框架或库的内部实现中。

一个非常典型的应用场景是动态代理(Dynamic Proxy)和AOP(Aspect-Oriented Programming)框架。比如,一些ORM(Object-Relational Mapping)框架为了实现延迟加载(Lazy Loading),可能会为实体类生成代理。这些代理类在运行时被创建,它们需要“看起来”和原始实体类一样,但它们的某些属性或方法访问会被拦截,以便在需要时才从数据库加载数据。

TypeDelegator
可以用来构建一个表示这种代理类型的
Type
对象,它能模拟原始类型的结构,同时注入拦截逻辑。

再比如,元数据注入或修改。假设你有一个第三方库,它通过反射来读取类的特定属性或方法,但你又无法修改这个第三方库的代码,也无法直接修改你自己的类定义(比如它来自一个编译好的程序集)。这时,你可以用

TypeDelegator
创建一个“包装类型”,在这个包装类型中,你可以动态地添加、移除或修改一些“虚拟”的自定义属性,或者改变方法的签名,从而欺骗那个第三方库,让它按照你的意图工作。这在某些复杂的插件系统或代码生成场景中,可能会派上用场。

它还可能被用于序列化/反序列化框架,当框架需要以一种非标准的方式来处理某些类型时,比如在序列化时隐藏某些属性,或者在反序列化时注入一些额外的逻辑,而又不想通过修改原始类型或使用复杂的特性来实现时。

如何使用
TypeDelegator
包装一个类型?

使用

TypeDelegator
的基本步骤是:继承它,并在构造函数中传入你想要包装的基类型。然后,重写你想要修改行为的
Type
成员。

下面是一个简单的示例,展示如何创建一个

TypeDelegator
的子类,它会改变被包装类型的
Name
,并过滤掉所有以
_Internal
结尾的方法:

using System;
using System.Reflection;

public class MyCustomTypeDelegator : TypeDelegator
{
    private readonly string _customName;

    // 构造函数:传入要包装的类型和自定义的名称
    public MyCustomTypeDelegator(Type delegatingType, string customName)
        : base(delegatingType)
    {
        _customName = customName;
    }

    // 重写 Name 属性,返回自定义的名称
    public override string Name
    {
        get { return _customName; }
    }

    // 重写 GetMethods 方法,过滤掉特定命名模式的方法
    public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
    {
        // 先获取原始类型的所有方法
        MethodInfo[] originalMethods = base.GetMethods(bindingAttr);

        // 过滤掉名称以 "_Internal" 结尾的方法
        var filteredMethods = new System.Collections.Generic.List();
        foreach (var method in originalMethods)
        {
            if (!method.Name.EndsWith("_Internal"))
            {
                filteredMethods.Add(method);
            }
        }
        return filteredMethods.ToArray();
    }

    // 你也可以重写其他成员,比如 GetProperties, GetFields, GetCustomAttributes 等
    // 比如,让 GetProperties 总是返回空数组,表示该类型没有公共属性
    // public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
    // {
    //     return new PropertyInfo[0];
    // }
}

// 示例用法
public class OriginalClass
{
    public string PublicProperty { get; set; }
    public void DoSomething() { Console.WriteLine("Doing something."); }
    public void DoSomething_Internal() { Console.WriteLine("This should be hidden."); }
    public static void StaticMethod() { Console.WriteLine("Static method."); }
}

public class Program
{
    public static void Main(string[] args)
    {
        Type originalType = typeof(OriginalClass);
        Console.WriteLine($"Original Type Name: {originalType.Name}"); // Output: OriginalClass

        // 使用自定义的 TypeDelegator 包装 OriginalClass
        Type wrappedType = new MyCustomTypeDelegator(originalType, "RenamedClass");
        Console.WriteLine($"Wrapped Type Name: {wrappedType.Name}"); // Output: RenamedClass

        Console.WriteLine("\n--- Original Methods ---");
        foreach (var method in originalType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
        {
            Console.WriteLine($"- {method.Name}");
        }
        /* Output:
         - DoSomething
         - DoSomething_Internal
         - StaticMethod
         - get_PublicProperty
         - set_PublicProperty
         - ToString
         - Equals
         - GetHashCode
         - GetType
        */

        Console.WriteLine("\n--- Wrapped Methods ---");
        foreach (var method in wrappedType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
        {
            Console.WriteLine($"- {method.Name}");
        }
        /* Output:
         - DoSomething
         - StaticMethod
         - get_PublicProperty
         - set_PublicProperty
         - ToString
         - Equals
         - GetHashCode
         - GetType
        (DoSomething_Internal 被过滤掉了)
        */

        // 尝试通过包装类型创建实例(注意:TypeDelegator本身不改变实际类型创建行为)
        // 如果你需要改变实例创建行为,那通常需要配合 Reflection.Emit 或其他代理生成技术
        try
        {
            var instance = Activator.CreateInstance(wrappedType);
            Console.WriteLine($"\nCreated instance of type: {instance.GetType().Name}"); // 仍然是 OriginalClass
            // 实际上,这里的 instance 仍然是 OriginalClass 的实例,
            // TypeDelegator 只是改变了反射层面上的“视图”,而不是对象的实际类型。
            // 如果要生成行为不同的代理对象,需要更复杂的动态代码生成。
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error creating instance: {ex.Message}");
        }
    }
}

这段代码展示了

TypeDelegator
如何改变一个类型在反射层面的表现。需要注意的是,
TypeDelegator
本身并不会改变你通过
Activator.CreateInstance(wrappedType)
创建出来的对象的实际类型。它依然会创建原始类型的实例。
TypeDelegator
的作用主要体现在反射查询上,它改变的是反射API(如
GetType()
GetMethods()
等)返回给你的信息。如果你需要生成一个行为上真正不同的代理对象,那通常需要结合
System.Reflection.Emit
或其他动态代码生成库来实现。
TypeDelegator
更像是为这些更复杂的动态生成提供了一个统一的
Type
接口,让你能以标准的方式来描述这些动态生成的类型。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1072

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

149

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1166

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

13

2026.01.19

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

356

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2078

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

348

2023.08.31

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

255

2023.09.05

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

31

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 51.4万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号