.NET进阶——深入理解泛型(2)泛型类泛型接口

8 阅读5分钟

泛型的核心价值是「一份代码适配多种类型」,除了之前讲的泛型方法,泛型还广泛应用于「泛型类」和「泛型委托」—— 前者解决 “类中属性 / 方法参数类型不固定” 的问题,后者解决 “委托类型与返回值 / 参数类型绑定” 的冗余问题。下面结合定义、示例、项目场景,详细完善并补充核心知识点:

一、泛型类:让类的属性 / 方法支持动态类型

1. 泛型类的定义

泛型类是指「定义时包含一个或多个类型占位符(如TU)」的类,这些类型占位符在实例化类时才明确具体类型。核心作用是:让类的属性、方法参数 / 返回值支持 “动态类型”,避免为不同类型重复创建相似类。

语法格式:

// T为类型占位符,可自定义名称(如TInfo、TData),通常用T表示
public class 类名<T>
{
    // 可将T用作属性类型
    public T  属性名 { get; set; }
    
    // 可将T用作方法参数/返回值类型
    public void 方法名(T 参数名) { }
    public T 方法名() { return default; }
}

2. 为什么需要泛型类?(解决非泛型类的局限性)

用户给出的非泛型Info类存在明显问题:ExtraInfo属性固定为string类型,无法适配其他类型(如DateTimeint、自定义类)。若要支持多种类型,非泛型方案只能重复创建类,导致代码冗余:

// 非泛型方案:为不同ExtraInfo类型创建多个类,冗余严重
class StringInfo { public int Id; public string Name; public string ExtraInfo; }
class DateTimeInfo { public int Id; public string Name; public DateTime ExtraInfo; }
class IntInfo { public int Id; public string Name; public int ExtraInfo; }

// 使用时需实例化不同类,繁琐
StringInfo info1 = new StringInfo { Id=1, Name="光泽", ExtraInfo="123" };
DateTimeInfo info2 = new DateTimeInfo { Id=2, Name="小香", ExtraInfo=DateTime.Now };

而泛型类只需定义一次,即可适配所有类型,完美解决冗余问题。

3. 泛型类的基础示例(完善用户代码)

public static void GenericClassDemo()
{
    // 1. 实例化泛型类:显式指定T为string,ExtraInfo只能存字符串
    Info<string> stu1 = new Info<string>
    {
        Id = 1,
        Name = "光泽",
        ExtraInfo = "学号:2023001" // 字符串类型附加信息
    };

    // 2. 显式指定T为DateTime,ExtraInfo只能存日期
    Info<DateTime> stu2 = new Info<DateTime>
    {
        Id = 2,
        Name = "小香",
        ExtraInfo = DateTime.Now // 日期类型附加信息
    };

    // 3. 显式指定T为int,ExtraInfo只能存整数
    Info<int> stu3 = new Info<int>
    {
        Id = 3,
        Name = "阿明",
        ExtraInfo = 95 // 整数类型附加信息(如成绩、积分)
    };

    // 4. 显式指定T为自定义类(如校园二手平台的Address类)
    Info<Address> stu4 = new Info<Address>
    {
        Id = 4,
        Name = "阿华",
        ExtraInfo = new Address { Province="广东", City="深圳" } // 自定义类类型
    };

    // 简化写法:使用var+类型推导(编译器自动根据赋值推导T)
    var stu5 = new Info<decimal> { Id=5, Name="阿强", ExtraInfo=999.9m };
}

// 泛型类:T为附加信息的类型占位符
public class Info<T>
{
    public int Id { get; set; }          // 固定类型(ID通常为int)
    public string Name { get; set; }     // 固定类型(姓名通常为string)
    public T ExtraInfo { get; set; }     // 动态类型(根据实例化时指定的T决定)
}

// 自定义类:用于演示T为引用类型的场景
public class Address
{
    public string Province { get; set; }
    public string City { get; set; }
    public override string ToString() => $"{Province}-{City}";
}

4. 泛型类的进阶:泛型约束(提升安全性)

和泛型方法一样,泛型类也支持约束,限制T的类型范围,避免传入非法类型导致错误。例如限制ExtraInfo只能是值类型(int、DateTime、decimal):

// 泛型约束:T必须是值类型(struct)
public class ValueTypeInfo<T> where T : struct
{
    public int Id { get; set; }
    public string Name { get; set; }
    public T ExtraInfo { get; set; }
}

// 正确:int是值类型
ValueTypeInfo<int> validInfo = new ValueTypeInfo<int> { Id=1, Name="小明", ExtraInfo=100 };

// 报错:string是引用类型,不符合struct约束
ValueTypeInfo<string> invalidInfo = new ValueTypeInfo<string> { Id=2, Name="小红" };

常见泛型类约束(同泛型方法):class(引用类型)、struct(值类型)、interface(实现接口)、new()(无参构造)等。

5. 项目实战

场景 1:商品附加信息存储

不同类型商品的附加信息不同(数码产品需 “保修期限”,书籍需 “作者”,家具需 “购买日期”),用泛型类统一管理:

// 泛型商品类:T为附加信息类型
public class Goods<T>
{
    public int GoodsId { get; set; }
    public string GoodsName { get; set; }
    public decimal Price { get; set; }
    public T ExtraInfo { get; set; } // 动态附加信息
}

// 实例化不同类型商品
var digitalGoods = new Goods<int> // 数码产品:附加信息=保修期限(月)
{
    GoodsId = 1001,
    GoodsName = "二手笔记本",
    Price = 2999.9m,
    ExtraInfo = 12 // 保修12个月
};

var bookGoods = new Goods<string> // 书籍:附加信息=作者
{
    GoodsId = 1002,
    GoodsName = "C#编程入门",
    Price = 29.9m,
    ExtraInfo = "张三"
};

var furnitureGoods = new Goods<DateTime> // 家具:附加信息=购买日期
{
    GoodsId = 1003,
    GoodsName = "二手书桌",
    Price = 199.9m,
    ExtraInfo = new DateTime(2023, 1, 1)
};

场景 2:通用数据返回结果

接口返回数据时,成功 / 失败的结构固定,但数据类型不同(如查询商品返回Goods,查询用户返回User),用泛型类统一封装:

// 泛型返回结果类
public class ApiResult<T>
{
    public bool Success { get; set; } // 是否成功
    public string Message { get; set; } // 提示信息
    public T Data { get; set; } // 动态数据(成功时返回)

    // 静态方法:快速创建成功/失败结果
    public static ApiResult<T> SuccessResult(T data) => new ApiResult<T> { Success=true, Data=data };
    public static ApiResult<T> FailResult(string message) => new ApiResult<T> { Success=false, Message=message };
}

// 接口使用示例
[HttpGet("goods/{id}")]
public ApiResult<Goods<string>> GetGoods(int id)
{
    var goods = _dbContext.Goods.FirstOrDefault(g => g.Id == id);
    if (goods == null)
        return ApiResult<Goods<string>>.FailResult("商品不存在");
    
    return ApiResult<Goods<string>>.SuccessResult(goods);
}

6. 泛型类的核心优势

  • 代码复用:一份类定义适配所有类型,无需重复创建相似类;
  • 类型安全:实例化时指定类型,编译时拦截非法赋值(如Info<string>ExtraInfo不能存DateTime);
  • 无性能损耗:避免非泛型类用object存储属性导致的装箱拆箱;
  • 灵活性强:支持自定义类型(如AddressGoods),适配复杂业务场景。