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

如何重构仅Equals实现不同的两个近乎相同的结构体?

建议方案:用泛型+策略模式抽象相等性逻辑

你不想用类,又要复用结构体的大部分代码同时区分相等性逻辑,接口是可行的,但更推荐泛型结合策略模式的方案——既避免重复代码,又保持值类型的性能优势,同时清晰分离相等性逻辑。

具体实现步骤

1. 定义相等性策略接口

先抽象出相等性判断和哈希计算的契约,所有不同的相等逻辑都实现这个接口:

public interface IEqualityStrategy<T>
{
    bool Equals(T x, T y);
    int GetHashCode(T obj);
}

2. 编写通用泛型结构体

把两个结构体的公共逻辑全部放到这个泛型结构体里,仅通过泛型参数注入不同的相等性策略:

public readonly struct GenericStruct<TStrategy> : IEquatable<GenericStruct<TStrategy>>
    where TStrategy : IEqualityStrategy<GenericStruct<TStrategy>>, new()
{
    // 这里放两个结构体共有的字段/属性(比如你的业务字段)
    public int Id { get; }
    public string Name { get; }

    // 静态复用策略实例,避免每次创建
    private static readonly TStrategy _equalityStrategy = new TStrategy();

    public GenericStruct(int id, string name)
    {
        Id = id;
        Name = name;
    }

    // 委托给策略实现相等判断
    public bool Equals(GenericStruct<TStrategy> other)
    {
        return _equalityStrategy.Equals(this, other);
    }

    public override bool Equals(object obj)
    {
        return obj is GenericStruct<TStrategy> other && Equals(other);
    }

    // 委托给策略计算哈希值
    public override int GetHashCode()
    {
        return _equalityStrategy.GetHashCode(this);
    }

    // 这里放两个结构体共有的其他方法,完全复用
    public void DoCommonBusinessLogic()
    {
        // 公共业务逻辑实现
    }
}

3. 实现不同的相等性策略

针对原来的AStruct和BStruct,分别实现对应的策略结构体:

// 对应原AStruct的相等逻辑
public struct AEqualityStrategy : IEqualityStrategy<GenericStruct<AEqualityStrategy>>
{
    public bool Equals(GenericStruct<AEqualityStrategy> x, GenericStruct<AEqualityStrategy> y)
    {
        // 原AStruct的Equals实现逻辑
        return x.Id == y.Id && string.Equals(x.Name, y.Name, StringComparison.Ordinal);
    }

    public int GetHashCode(GenericStruct<AEqualityStrategy> obj)
    {
        // 原AStruct的GetHashCode实现逻辑
        return HashCode.Combine(obj.Id, obj.Name);
    }
}

// 对应原BStruct的相等逻辑
public struct BEqualityStrategy : IEqualityStrategy<GenericStruct<BEqualityStrategy>>
{
    public bool Equals(GenericStruct<BEqualityStrategy> x, GenericStruct<BEqualityStrategy> y)
    {
        // 原BStruct的Equals实现逻辑(比如忽略Name的大小写)
        return x.Id == y.Id && string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
    }

    public int GetHashCode(GenericStruct<BEqualityStrategy> obj)
    {
        // 原BStruct的GetHashCode实现逻辑(对应忽略大小写的哈希)
        return HashCode.Combine(obj.Id, obj.Name.ToLowerInvariant());
    }
}

4. 使用方式

直接通过泛型参数指定策略,得到和原结构体功能一致的类型:

// 等价于原AStruct
var aInstance = new GenericStruct<AEqualityStrategy>(1, "Test");
// 等价于原BStruct
var bInstance = new GenericStruct<BEqualityStrategy>(1, "test");

为什么不直接用普通接口?

如果直接让两个结构体实现同一个自定义相等接口,会存在两个问题:

  1. 装箱性能损耗:当你通过接口类型调用Equals时,值类型结构体会被装箱,影响性能;
  2. 代码重复无法避免:两个结构体的公共字段、方法还是要重复写,达不到复用目的。

而泛型+策略模式的方案,既通过接口抽象了相等性逻辑,又完全复用了结构体的公共代码,同时因为策略是值类型且通过静态字段复用,不会产生装箱开销,完美适配你的需求。

备选方案:源码生成器

如果追求极致性能,不想有任何策略调用的间接开销,可以用Roslyn源码生成器,基于模板自动生成两个结构体,仅替换Equals和GetHashCode的实现部分。这种方式代码零重复,性能和手写结构体完全一致,但需要编写生成器代码,适合需要大量类似结构体的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 09:50:51