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

CS0019: Operator '!=' cannot be applied to operands of type 'TEnum' and 'TEnum' where TEnum: Enum #2025

Closed
voroninp opened this issue Nov 26, 2018 · 10 comments

Comments

@voroninp
Copy link

voroninp commented Nov 26, 2018

private static object[] GetEnumLookupData<TEnum>() where TEnum: Enum
{
    var data = Enum.GetValues(typeof(TEnum))
        .OfType<TEnum>()
        .Where(e => e != default(TEnum))
        .Select(e => new {Id = e, Name = e.ToString()})
        .ToArray();

    return data;
}

This does not compile, but returns a error:

CS0019: Operator '!=' cannot be applied to operands of type 'TEnum' and 'TEnum'

What am I missing here? Isn't it clear that TEnum is a value type?

The funniest thing is that if I omit (TEnum) in default(TEnum), so it is now e != default it compiles, but default generates porbably just a null because I see 0 value in created EF Core migration. WTF?!

I tried to use EqualityComparer<TEnum>.Default.Equals(...) and it kinda worked, however, EF Core 2.1 has a bug: migrations with seeding of data with emun as primary key are unstable. This bug is fixed only in EF Core 2.2 preview.
So I changed to int and ...

private static object[] GetEnumLookupData<TEnum>() where TEnum: struct, Enum
{
    var comparer = EqualityComparer<TEnum>.Default;

    var data = Enum.GetValues(typeof(TEnum))
        .OfType<TEnum>()
        .Where(e => !comparer.Equals(e, default))
        .Select(e => new {Id = (int)e, Name = e.ToString()})
        .ToArray();

    return data;
}

CS0030 Cannot convert type 'TEnum' to 'int'

@YairHalberstadt
Copy link
Contributor

YairHalberstadt commented Nov 26, 2018

Why not just !e.Equals(default(TEnum))

@chrisoverzero
Copy link

chrisoverzero commented Nov 26, 2018

What am I missing here? Isn't it clear that TEnum is a value type?

No, because it doesn’t say so in the function definition. What changes if you change the definition to this?

private static object[] GetEnumLookupData<TEnum>() where TEnum : struct, Enum

After your edit:

CS0030 Cannot convert type 'TEnum' to 'int'

Not all enums are backed by Int32.

@voroninp
Copy link
Author

@chrisoverzero This does not help.
@YairHalberstadt Why do I need boxing one more time? EqualitComparer exists just for this.

@HaloFour
Copy link
Contributor

HaloFour commented Nov 26, 2018

The C# compiler doesn't treat the System.Enum constraint in any special manner. It doesn't even consider it, by itself, to be a value type, because System.Enum itself is not a value type and you may use System.Enum as the generic argument. If you combine the constraints System.Enum and struct then you do force the compiler and runtime to only allow actual enum types, but the compiler still doesn't consider them to be any different than normal structs and doesn't permit arithmetic operations. The support for System.Enum as a generic type constraint in C# 7.x was only to lift the imposed limitation and allow what the CLR already permitted.

@chrisoverzero
Copy link

chrisoverzero commented Nov 26, 2018

This does not help.

Sure it did. Now you have a different problem, one caused by EF. Not all enum values can be cast to Int32. They can be backed by different numeric types.

I'm not sure there’s any problem here for csharplang to solve.

@voroninp
Copy link
Author

voroninp commented Nov 26, 2018

@chrisoverzero No it did not. Try the first version without int yourself.

That with e != default(TEnum)

@chrisoverzero
Copy link

chrisoverzero commented Nov 26, 2018

@voroninp You are applying multiple solutions at once, confusing both me and the issue.

To get default(TEnum) to do what you want it to do – that is, to be the 0 value of the enum represented by TEnum – the type signature I suggested will work. This solves these problems you came up against:

Isn't it clear that TEnum is a value type?

[…] but default generates porbably just a null […]

This frees you up to apply one of the equality checking suggestions from this thread, the instance method or the default equality comparer.

How to solve the EF problem, I have no idea. An enum as a primary key strikes me as super odd, though. Perhaps you could use the name of the value as the key, but this is outside the scope of csharplang.

@voroninp
Copy link
Author

@chrisoverzero > the type signature I suggested will work

Did you try it?! It does not compile.

private static object[] GetEnumLookupData<TEnum>() where TEnum: struct, Enum
{
    var data = Enum.GetValues(typeof(TEnum))
        .OfType<TEnum>()
        .Where(e => e != default(TEnum))
        .Select(e => new {Id = e, Name = e.ToString()})
        .ToArray();

    return data;
}

@chrisoverzero
Copy link

chrisoverzero commented Nov 26, 2018

@voroninp I addressed the problem of equality separately.

This frees you up to apply one of the equality checking suggestions from this thread, the instance method or the default equality comparer.

The reasons for this given by @HaloFour are correct.

@voroninp
Copy link
Author

@chrisoverzero Ok, my implicit initial assumption was wrong. Structs do not have == operators implemented by default, hence, it won't work with != default anyway.

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

4 participants