What does this mean(Generics)

T

Tony

Hello!

It says "Another limitation that you need to be aware of is that using the
operator == and != are
only permitted when comparing a value of a type supplied to a generic type
to null.
That is, the following code works.
Here if T is a value type then it is always assumed to be non-null, so in
the above
code Compare always return true;"

The question is what does they mean with the text saying "using the operator
== and != are
only permitted when comparing a value of a type supplied to a generic type
to null."

public bool Compare(T op1, T op2)
{
if (op1 != null && op2 != null)
{
return true;
}
else
{
return false;
}
}

//Tony
 
J

Jon Skeet [C# MVP]

It says "Another limitation that you need to be aware of is that using the
operator == and != are
only permitted when comparing a value of a type supplied to a generic type
to null.
That is, the following code works.
Here if T is a value type then it is always assumed to be non-null, so in
the above
code Compare always return true;"

The question is what does they mean with the text saying "using the operator
== and != are
only permitted when comparing a value of a type supplied to a generic type
to null."

It means that if T is unconstrained you can't do:

T foo = ...;
T bar = ...;

if (foo == bar)

If you constrain T to be a reference type (i.e. use "where T : class")
then reference identity comparisons will be used and you can compare
two values

If you constrain T to be derived from a type which overloads == and !=
then those overloads will be used. This is *not* polymorphic - only
overloads the compiler can guarantee at compile-time are used. For
instance, if you had a constraint "T : IEnumerable<char>" and used
"string" then == would still mean reference identity, rather than
using the == overloaded for string.

Jon
 
Q

qglyirnyfgfo

What I find interesting about this example is that if you constrain T
to be a “struct” then you will get a compile error.

So why is it that when T its unconstrained and it can be a “class” –
or- **struct** you don’t get a compile error?
 
J

Jon Skeet [C# MVP]

What I find interesting about this example is that if you constrain T
to be a =3Fstruct=3F then you will get a compile error.

So why is it that when T its unconstrained and it can be a =3Fclass=3F =3F
or- **struct** you don=3Ft get a compile error?

When it's unconstrained and at execution time T is a struct, then the
comparison with null always fails - even for default(T). (That's a
handy way of telling at execution time whether or not it *is* a struct,
in fact.)

If you explicitly constrain it to being a struct, then the comparison
is pointless as you always know the answer.
 
Q

qglyirnyfgfo

I am ok with what you are saying, however, it was my impression that
the whole reason for constraining a generic parameter was so that the
compiler could resolve any issues during compiling time.

If the compiler allows the code to compile without a constraint
(therefore allowing the generic parameter to by “class” –or-
“struct”), then in my opinion it should allow it to compile with a
constraint of “struct” (whether that makes sense or not) otherwise
this is a double standard.

If I wanted to know if the generic parameter is a “class” or a
“struct” then I could use “typeof(T).IsClass”.

I am still not clear why this is allowed. Am I just not looking a this
the right way?

Thanks.
 
J

Jon Skeet [C# MVP]

I am ok with what you are saying, however, it was my impression that
the whole reason for constraining a generic parameter was so that the
compiler could resolve any issues during compiling time.

To some extent. I'd say the reasons are:

1) Allow *more* to be done by the compiler within the type/method,
because it has more information about what the type parameter might
be at execution time.

2) Constrain clients so they'll only use the type/method with
appropriate type arguments.
If the compiler allows the code to compile without a constraint
(therefore allowing the generic parameter to by =3Fclass=3F =3For-
=3Fstruct=3F), then in my opinion it should allow it to compile with a
constraint of =3Fstruct=3F (whether that makes sense or not) otherwise
this is a double standard.

No - it's using the fact that it's got more information to prohibit a
redundant comparison. The comparison *isn't* redundant when the type is
unconstrained, as it will have different results depending on the value
and the type parameter.
If I wanted to know if the generic parameter is a =3Fclass=3F or a
=3Fstruct=3F then I could use =3Ftypeof(T).IsClass=3F.

You could, but I *suspect* it takes longer. In particular, the JIT can
work out that

if (default(T) == null)

will always be false for value types and true for reference types
without knowing anything about the Type class. I would hope that it
would take advantage of that and optimise away the comparison (and
potentially the code within the subsequent block).

Now it *could* do that through knowledge of the IsClass property, but
it's a bit further away from its normal type of optimisation, IMO.
I am still not clear why this is allowed. Am I just not looking a this
the right way?

Would you rather the comparison was prohibited completely for
unconstrained type parameters? While I can't *immediately* think of a
time I've used it, I'm sure I have...
 
Q

qglyirnyfgfo

Would you rather the comparison was prohibited completely for
unconstrained type parameters?


I think it should be either *complete prohibited* or *completely
allowed* to compare value types to nulls.

Take a look at the following snippet of code:

int i = 8;
bool b = (i == null);

If you compile this code the compiler will compile it successfully and
only issue a warning to the effect that the statement is always false.
So again, it ok to let it fly here but not on the example we have been
talking about.

In my opinion, the compiler should either allow a value type be
compared to a null or not allow it (whether that makes sense or not).
It should not pick and choose sometime yes, sometimes no based on some
unknown reasoning.

But something tells me you are not ok with that or are you? :)
 
J

Jon Skeet [C# MVP]

I think it should be either *complete prohibited* or *completely
allowed* to compare value types to nulls.

Take a look at the following snippet of code:

int i = 8;
bool b = (i == null);

If you compile this code the compiler will compile it successfully and
only issue a warning to the effect that the statement is always false.
So again, it ok to let it fly here but not on the example we have been
talking about.

Well, that's a somewhat completely different issue - that's doing an
implicit conversion from int to int? and then comparing *that* with
null.
In my opinion, the compiler should either allow a value type be
compared to a null or not allow it (whether that makes sense or not).
It should not pick and choose sometime yes, sometimes no based on some
unknown reasoning.

It's not unknown. It's all in the spec.
But something tells me you are not ok with that or are you? :)

I'm pretty much fine with the way it is now, although the implicit
conversion above does irritate me.
 
Q

qglyirnyfgfo

Well, that's a somewhat completely different issue - that's doing an
implicit conversion from int to int? and then comparing *that* with
null.

mmm.. the plot thickens. This calls fro doing some more research… I am
looking at the IL and seeing interesting things.

Go to go home, will post later.

Thanks for the help!
 
M

Marc Gravell

Feel free to validate it independently and click some buttons to help
escalate it, then ;-p

Marc
 
J

Jon Skeet [C# MVP]

Feel free to validate it independently and click some buttons to help
escalate it, then ;-p

Have done so - but in testing it I found that it's not quite as bad as
it might be. It occurs with custom structs such as DateTime, but if
you try it with primitive types (e.g. int) the warning is still
raised. As the primitive types are used so much more (IME) than other
structs, that's a relief - but I'd still like to see it fixed.

Jon
 
M

Marc Gravell

I observed it didn't happen with primatives, which is why I posted with
a bespoke struct - I didn't think of trying DateTime/decimal. That would
have saved some typing ;-p

Cheers,

Marc
 
B

Ben Voigt [C++ MVP]

Jon said:
Have done so - but in testing it I found that it's not quite as bad as
it might be. It occurs with custom structs such as DateTime, but if
you try it with primitive types (e.g. int) the warning is still
raised. As the primitive types are used so much more (IME) than other
structs, that's a relief - but I'd still like to see it fixed.

Jon

I can't think of an example offhand, but it seems legal, right now, to
define a meaningful operator== comparison between different types, including
between a value type and a reference type.

Oh, here's the counter-example -- IntPtr. Although inspection reveals that
IntPtr.Zero == null would indeed return false. And that not producing
either a warning or the least-surprising result is more than a little
disturbing.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top