导读:本期聚焦于小伙伴创作的《非泛型类在C#中怎么用?实例详解带你理解非泛型编程》,敬请观看详情。把ArrayList当作对象容器存放不同类型数据,常常在运行时才暴露类型转换错误,这种隐患让不少项目吃过亏。非泛型类指不通过类型参数约束成员类型的类,典型如早期.NET类库中的ArrayList与Hashtable。本文结合具体代码演示非泛型类的声明方式、构造逻辑与常见调用手段,说明它为何在缺乏泛型支持的时代承担基础数据组织工作。你会看到非泛型在灵活性上的优势,以及装箱拆箱带来的性能损耗,从而在实际维护旧代码或编写简单工具时做出合适选择。

在C#语言发展早期,开发者处理集合与通用对象容器时主要依赖非泛型类。非泛型类是指在定义时不使用类型参数、其内部成员以object类型接纳任意数据的类。这类类在.NET Framework 1.0时代广泛使用,虽然现在泛型已成主流,但理解非泛型类的运行机制对维护遗留系统、阅读基础类库源码仍有价值。下面通过具体例子说明非泛型类的结构与实际用法。

一、什么是非泛型类

非泛型类与泛型类的最大区别在于:泛型类通过<T>这类类型参数在编译期锁定数据类型,而非泛型类直接把字段、属性或方法参数声明为object。因为object是所有类型的基类,所以非泛型类可以接收任何实例,这种写法带来了极大的灵活度,但也埋下了类型安全的隐患。

以最常见的非泛型集合ArrayList为例,它内部维护一个object数组,添加元素时统一转为object存储。当你取出元素时,必须手动强制转换回原类型,一旦转换类型不匹配就会抛出InvalidCastException。以下代码展示了一个自定义非泛型缓存类的简单声明:

using System;
using System.Collections;

// 自定义非泛型缓存类,可存任意对象
public class SimpleCache
{
    private Hashtable _store = new Hashtable();

    // 放入缓存,key为字符串,value为任意对象
    public void Put(string key, object value)
    {
        _store[key] = value;
    }

    // 取出缓存,返回object需调用方自行转换
    public object Get(string key)
    {
        return _store[key];
    }
}

上面这段代码没有使用任何类型参数,Put方法接收object类型的value,因此既能存字符串也能存整数。这种写法在不确定数据种类的场景下很方便,但正如后面会提到的,它牺牲了编译期检查能力。

二、非泛型类的实例创建与调用

非泛型类的实例化过程和普通类完全一致,使用new关键字调用其构造函数即可。由于类型没有参数化,构造时不需要指定类型实参。我们基于前面的SimpleCache类,演示如何存入和读取不同类型的数值。

下面代码向缓存分别放入一个字符串与一个整数,读取时分别转换。注意转换时必须使用正确类型,否则运行时会报错。这种使用方式在旧版代码中十分普遍,也是很多初学者接触集合的第一步。

using System;

class Program
{
    static void Main()
    {
        SimpleCache cache = new SimpleCache();
        cache.Put("name", "张三");
        cache.Put("age", 28);

        // 读取并转换
        string name = (string)cache.Get("name");
        int age = (int)cache.Get("age");

        Console.WriteLine("姓名:" + name);
        Console.WriteLine("年龄:" + age);

        // 若误转换类型,如将age当string取,运行时会异常
        // string wrong = (string)cache.Get("age");
    }
}

从调用过程可以看出,非泛型类在写法上非常直接,不需要关心类型参数。但这种自由是有代价的:编译器无法帮你确认Get返回的是否真是目标类型,错误只能留到程序运行后才被发现。

三、非泛型类的优点与缺陷

非泛型类最明显的优点是兼容性强。在泛型尚未出现的.NET版本中,它是实现通用容器的唯一手段。即便现在,当你需要快速写一个只处理少量数据、类型混杂的小工具,或者对接某些只暴露object接口的旧API时,非泛型类依然顺手。

但它的缺陷同样突出。首先是类型不安全,如前文代码所示,取出数据时的强制转换可能在运行时失败。其次是性能问题:值类型存入object字段会发生装箱,取出时要拆箱,频繁操作会产生大量临时对象和GC压力。下列表格对比了非泛型与泛型在核心维度的差异:

对比维度非泛型类泛型类
类型安全编译期不检查,运行期可能异常编译期确定类型,错误早暴露
性能表现值类型需装箱拆箱无装箱拆箱开销
使用灵活性可接收任意类型限定为指定类型或其子类

因此在新项目中,官方推荐优先使用List<T>、Dictionary<TKey, TValue>等泛型类型。但在阅读早期代码或封装兼容层时,掌握非泛型类的实例写法能帮你少走弯路。

四、非泛型类在实践中的注意事项

如果你不得不在生产代码中编写非泛型类,建议对公开方法做必要的类型判断,用is或as运算符降低崩溃风险。例如Get方法可改为尝试转换并返回是否成功的模式,让调用方更安全地处理结果。

此外,非泛型类常与反射配合实现通用逻辑,比如根据配置动态创建实例并缓存。此时要注意object到具体类型的转换集中在一处管理,避免散落在各业务方法里难以排查。以下示例展示带类型检查的改进版Get:

using System;

public class SafeCache
{
    private Hashtable _store = new Hashtable();

    public void Put(string key, object value)
    {
        _store[key] = value;
    }

    // 安全获取,转换失败返回null且不抛异常
    public T Get<T>(string key) where T : class
    {
        object val = _store[key];
        return val as T;
    }
}

这个改进版本虽然引入了泛型方法,但类本身仍是非泛型结构,兼顾了旧有容器与基础类型安全。对于纯非泛型场景,也可在内部用is判断后抛出自描述异常,提升可维护性。

五、总结

非泛型类通过object类型实现了最大程度的通用性,是.NET早期重要的编程构件。它的实例详解核心在于理解其无类型参数、运行时转换、装箱损耗三大特征。在现代化开发中,除非维护老系统或写临时脚本,否则应倾向泛型方案。弄清非泛型类的原理与用法,能让你在遭遇ArrayList、Hashtable等旧类型时从容应对,也更能体会泛型设计带来的编译期保障与性能优化。

非泛型实例详解CSharp修改时间:2026-08-02 20:09:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。