HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Generic wrapper for equality and hash implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
genericequalityhashwrapperforandimplementation

Problem

I was writing an answer in another thread and came across this challenge. I'm trying to have a generic class so that I can delegate the routine (and tiring) Equals and GetHashCode implementation to it (which should handle all that). With the code things will be clearer.

Please note that I have no problem with performance as of now. But when writing a generic library for future use, I'm contemplating better designs. I'll include the bare minimum code required to drive the idea.

Approach 1

public class Equater : IEqualityComparer
{
    public IEnumerable> Keys { get; private set; }

    public Equater(params Func[] keys)
    {
        Keys = keys;
    }

    public bool Equals(T x, T y)
    {
        ----
    }

    public int GetHashCode(T obj)
    {
       .....
    }
}

//an example usage
public class Dao : IEquatable
{
    static Equater equater = new Equater(x => x.Id, x => x.Table);

    public bool Equals(Dao other)
    {
        return equater.Equals(this, other);
    }

    public override int GetHashCode()
    {
        return equater.GetHashCode(this);
    }
}


This is great considering it works for any number of properties. But performance sucks, boxing I believe is the culprit. Runs in around 260 ms for about 100000 calls to GetHashCode.

Approach 2

public static class Equater
{
    public static Func equals;
    public static Func getHashCode;

    public static void Set(Func key1Selector, 
                                         Func key2Selector)
    {
        equals = (x, y) =>
        {
            ----
        };
        getHashCode = t => { .... };
    }

    //other overloads of Set with varying type arguments
}

//an example usage
public class Dao : IEquatable
{
    static Dao()
    {
        Equater.Set(x => x.Id, x => x.Table);
    }

    public bool Equals(Dao other)
    {
        return Equater.equals(this, other);
    }

    public override int GetHashCode()
    {
        return Equater.getHashCode(this);
    }
}


Solution

You can reach maximum performance by generating specialized code at runtime using Expression trees. Instead of injecting Funcs, inject Expression> s into your Equater class. Analyze what field they point to and build up the corresponding expressions for hashing and equality.

That way you only have to execute one static field load and one delegate call per call to Equals or GetHashCode.

Context

StackExchange Code Review Q#25131, answer score: 3

Revisions (0)

No revisions yet.