公司系统中有一接口访问量大,内部计算逻辑较为复杂。在优化时打算把Request中的参数做为Key,Response做为Value放到进程内缓存中,以降低服务器压力,提高接口响应速度。因为Response中一些数据时效性要求较高,所以缓存设置一个较短的过期时间(比如10s)。

但这里牵涉到一个问题,如何有效的判断两次请求的参数是相等的。C#中自定义类型会从Object类继承Equals和GetHashCode两个方法,可以根据实际需求来重写这两个方法实现对象相等性比较。

Object.Equals(Object)

.NET 中不同类型对于Equals方法的默认实现如下:

Type category Equality defined by Comments
Class derived directly from Object Object.Equals(Object) Reference equality; equivalent to calling Object.ReferenceEquals.
Structure ValueType.Equals Value equality; either direct byte-by-byte comparison or field-by-field comparison using reflection.
Enumeration Enum.Equals Values must have the same enumeration type and the same underlying value.
Delegate MulticastDelegate.Equals Delegates must have the same type with identical invocation lists.
Interface Object.Equals(Object) Reference equality.

Object

通过源码,可以看到Object中Equals方法的实现,即.NET中所有类型的默认实现:

 
从系统性能优化谈对象相等性

ValueType

反编译之后,可以看到ValueType中Equals方法的实现,即值类型的默认实现,它重写了Object.Equals方法:

 
从系统性能优化谈对象相等性

上面可以看到,ValueType中Equals实现思路如下:

  • obj==null返回false

  • 若this和obj的运行时类型不同则返回false

  • 如果值类型中包含的字段均是值类型则逐字节比较字段值

  • 若含有引用类型字段,则使用使用反射获取字段信息,然后调用字段的Equals方法来逐字段比较相等性

重写Equals

Object的Equals仅通过引用来比较相等性。应该说是identity而非equality,与Python中的is、== 操作符类似;ValueType的Equals中使用了反射性能较差。这种默认实现通常不能满足需求,自定义实现Equals思路如下:

  • obj为null,返回false,因为Equals是实例方法,this不会为null

  • 对于引用类型,this和obj引用同一个对象返回true

  • 调用GetType方法来判断this和obj在运行时是否是相同类型

  • 必要时调用基类的Equals方法来比较基类中字段的相等性(通常不调用Object类的Equals)

  • 调用Equals方法逐字段进行比较

根据上述思路,实现自定义类型的Equals方法:

 
从系统性能优化谈对象相等性
public class Entity
{
    public Entity(string tag, int count, IDictionary<string, string> descriptioins)
    {
        this.Tag = tag;
        this.Count = count;
        this.Descriptions = descriptioins;
    }

    public string Tag { private set; get; }

    public int Count { private set; get; }

    public IDictionary<string, string> Descriptions { private set; get; }
    /// <summary>
    /// 逐字段比较相等性
    /// </summary>
    public override bool Equals(object obj)
    {
        if (obj == null)
        {
            return false;
        }

        if (object.ReferenceEquals(this, obj))
        {
            return true;
        }

        // 这里判断this与obj在运行时类型是否一样
        // 使用is关键字进行类型判断的话,如果obj是Entity的子类也会返回true
        // 如果类型被标记为sealed,可以使用is来判断
        if (this.GetType().Equals(obj.GetType()) == false)
        {
            return false;
        }

        var other = obj as Entity;
        if (other == null)
        {
            return false;
        }
        if (this.Tag != other.Tag)
        {
            return false;
        }
        if (this.Count != other.Count)
        {
            return false;
        }
        if (this.Descriptions.FieldsEquales(other.Descriptions) == false)
        {
            return false;
        }

        return true;
    }
    /// <summary>
    /// 得到的哈希值应在对象生命周期中保持不变
    /// </summary>
    public override int GetHashCode() => this.ToString().GetHashCode();
    /// <summary>
    /// 含义同Equals(object obj)
    /// </summary>
    public static bool operator ==(Entity left, Entity right)
    {
        // The null keyword is a literal that represents a null reference, one that does not refer to any object. 
        // null is the default value of reference - type variables.Ordinary value types cannot be null, except for nullable value types.
       if (object.ReferenceEquals(left, null))
       {
           return object.ReferenceEquals(right, null);
       }

       return left.Equals(right);
    }
    /// <summary>
    /// 含义与==相反
    /// </summary>
    public static bool operator !=(Entity left, Entity right) => !(left == right);

    public override string ToString() => JsonConvert.SerializeObject(this);
}
override Equals

 

从系统性能优化谈对象相等性
public static class DictionaryExtension
{
    /// <summary>
    /// 调用Object.Equals(Object)方法逐个字段进行相等性比较
    /// <para>双方均为null时返回true,一方为null是返回false</para>
    /// </summary>
    public static bool FieldsEquals<TKey, TValue>(this IDictionary<TKey, TValue> source, IDictionary<TKey, TValue> target)
    {
        if (source == null && target == null)
        {
            return true;
        }
        if (source == null || target == null)
        {
            return false;
        }
        if (object.ReferenceEquals(source, target))
        {
            return true;
        }
        if (source.Keys.Count != target.Keys.Count)
        {
            return false;
        }
        foreach (var key in source.Keys)
        {
            if (target.ContainsKey(key) == false)
            {
                return false;
            }
            var sourceValue = source[key];
            var targetValue = target[key];
            if (object.ReferenceEquals(sourceValue, null))
            {
               if (object.ReferenceEquals(targetValue, null))
               {
                   continue;
               }

                return false;
            }

           if (sourceValue.Equals(targetValue))
           {
                continue;
           }
           return false;
        }
        return true;
    }
}
Directory FieldsEquals

 

相关文章:

  • 2021-12-07
  • 2022-12-23
  • 2021-08-14
  • 2022-12-23
  • 2022-12-23
  • 2022-02-15
猜你喜欢
  • 2021-11-19
  • 2022-01-31
  • 2022-12-23
  • 2021-12-14
  • 2022-01-10
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案