Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'Generate GetHashCode' generates a call to ValueType.GetHashCode #26141

Closed
jnm2 opened this issue Apr 13, 2018 · 5 comments
Closed

'Generate GetHashCode' generates a call to ValueType.GetHashCode #26141

jnm2 opened this issue Apr 13, 2018 · 5 comments

Comments

@jnm2
Copy link
Contributor

jnm2 commented Apr 13, 2018

VS 15.6.6.

If you use Roslyn's 'Generate GetHashCode...' suggestion on a struct, it asks you which properties to include, and then it generates code combining those with an additional hash, base.GetHashCode():

struct Foo
{
    public Type FixtureType { get; }
    public MethodInfo Method { get; }

    public override int GetHashCode()
    {
        var hashCode = 662238274;
        hashCode = hashCode * -1521134295 + base.GetHashCode();
        hashCode = hashCode * -1521134295 + EqualityComparer<Type>.Default.GetHashCode(FixtureType);
        hashCode = hashCode * -1521134295 + EqualityComparer<MethodInfo>.Default.GetHashCode(Method);
        return hashCode;
    }
}

Just like when inheriting from System.Object, inheritors from System.ValueType are always going to want to replace the base has calculation not combine it. Not only can this easily result in incorrect hashes which contradict the Equals implementation, many times the entire purpose of overriding ValueType.GetHashCode is to provide a better-performing implementation.

@jnm2
Copy link
Contributor Author

jnm2 commented Apr 13, 2018

So, stepping back, if you've ever used (a recent?) Roslyn to generate GetHashCode for a struct which uses non-default equality, you have a bug to go fix. The generated implementation breaks the invariant that if Equals returns true for two objects, their hash codes should be equal. You'll get false negatives for dictionary keys, or groups with duplicate keys, among other things.

@jnm2
Copy link
Contributor Author

jnm2 commented Apr 13, 2018

It looks like @CyrusNajmabadi has done something here which made it into master two months ago. Think the problem is already fixed in master?

https://github.com/dotnet/roslyn/pull/24161/files#diff-a0d90f84d4625c53558ee000fb8ed2bbR147

@jmarolf
Copy link
Contributor

jmarolf commented Apr 13, 2018

@jnm2 this problem should already be fixed in the latest Visual Studio 15.7 Preview

If you generate hashcode + equals on the following struct

using System;
using System.Collections.Generic;
using System.Reflection;

struct Foo
{
    public Type FixtureType { get; }
    public MethodInfo Method { get; }
}

you get this:

using System;
using System.Collections.Generic;
using System.Reflection;

struct Foo
{
    public Type FixtureType { get; }
    public MethodInfo Method { get; }

    public override bool Equals(object obj)
    {
        if (!(obj is Foo))
        {
            return false;
        }

        var foo = (Foo)obj;
        return EqualityComparer<Type>.Default.Equals(FixtureType, foo.FixtureType) &&
               EqualityComparer<MethodInfo>.Default.Equals(Method, foo.Method);
    }

    public override int GetHashCode()
    {
        var hashCode = 662238274;
        hashCode = hashCode * -1521134295 + EqualityComparer<Type>.Default.GetHashCode(FixtureType);
        hashCode = hashCode * -1521134295 + EqualityComparer<MethodInfo>.Default.GetHashCode(Method);
        return hashCode;
    }
}

@jnm2
Copy link
Contributor Author

jnm2 commented Apr 13, 2018

Closing as fixed in 15.7.

@jnm2 jnm2 closed this as completed Apr 13, 2018
@CyrusNajmabadi
Copy link
Member

This was fixed with #24161

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants