You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建可容纳多种泛型IThingy类型的C#枚举集合?

问题描述

我定义了一个带泛型的接口IThingy<T>,包含一组共享属性:

public interface IThingy<T>
{
    public string Name { get; init; }
    public int Priority { get; init; }
    public T Value { get; set; }
}

多个类实现了这个接口,例如:

public class TextThingy : IThingy<string?>
{
    public string Name { get; init; } = "A Text Thingy";
    public int Priority { get; init; }
    public string? Value { get; set; }
}

public class NumberThingy: IThingy<int?>
{
    public string Name { get; init; } = "A Number Thingy";
    public int Priority { get; init; }
    public int? Value { get; set; }
}

现在需要一个ThingyGroup类来存放不同类型的IThingy<T>实例集合,用于遍历生成API的JSON:

public class ThingyGroup 
{
    public string Name { get; init; } = "A Group of Related Thingies";
    public IEnumerable<IThingy<???>> Thingies { get; init; } = new List<IThingy<???>>();
}

但直接省略类型参数会报错,设为object或dynamic时,添加TextThingy或NumberThingy实例到列表也会报错,请问这里应该用什么类型才能让列表容纳不同类型的Thingy?

解决方案

方法1:定义非泛型基接口

这是最通用且无局限性的方案,通过创建非泛型基接口统一共享属性,让泛型接口继承它:

// 非泛型基接口,存放无需泛型约束的共享属性
public interface IThingy
{
    string Name { get; init; }
    int Priority { get; init; }
    // 新增非泛型Value属性,用于统一访问值(兼容序列化)
    object? Value { get; }
}

// 泛型接口继承非泛型基接口
public interface IThingy<T> : IThingy
{
    // 使用new关键字覆盖基接口的Value属性,保留泛型类型的读写能力
    new T Value { get; set; }
}

实现类需要显式实现非泛型接口的Value属性,确保类型转换正确:

public class TextThingy : IThingy<string?>
{
    public string Name { get; init; } = "A Text Thingy";
    public int Priority { get; init; }
    public string? Value { get; set; }

    // 显式实现非泛型接口的Value属性
    object? IThingy.Value => Value;
}

public class NumberThingy: IThingy<int?>
{
    public string Name { get; init; } = "A Number Thingy";
    public int Priority { get; init; }
    public int? Value { get; set; }

    object? IThingy.Value => Value;
}

最后ThingyGroup就可以使用非泛型的IThingy类型:

public class ThingyGroup 
{
    public string Name { get; init; } = "A Group of Related Thingies";
    public IEnumerable<IThingy> Thingies { get; init; } = new List<IThingy>();
}

此时所有实现IThingy<T>的类实例都能添加到Thingies集合中,遍历和JSON序列化(如System.Text.Json、Newtonsoft.Json)都能正常工作,同时保留Value的读写能力。

方法2:利用泛型协变(只读场景专用)

如果业务场景中Value属性只需要读取、不需要修改,可以将泛型接口声明为协变(使用out关键字):

// 协变泛型接口,Value仅保留只读属性
public interface IThingy<out T>
{
    string Name { get; init; }
    int Priority { get; init; }
    T Value { get; } // 移除set访问器
}

此时ThingyGroup可以将类型参数设为object:

public class ThingyGroup 
{
    public string Name { get; init; } = "A Group of Related Thingies";
    public IEnumerable<IThingy<object>> Thingies { get; init; } = new List<IThingy<object>>();
}

协变特性允许将IThingy<string?>、IThingy<int?>等隐式转换为IThingy<object>,从而存入同一集合。但该方案的局限性很明显:必须牺牲Value的写能力,仅适用于只读场景。

内容的提问来源于stack exchange,提问作者Kevin Whiteside

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 12:05:44