Why is this test expression an error? - c#

i want to understand why the C# language decided to make this test expression as an error.
interface IA { }
interface IB { }
class Foo : IA, IB { }
class Program
{
static void testFunction<T>(T obj) where T : IA, IB
{
IA obj2 = obj;
if (obj == obj2) //ERROR
{
}
}
static void Main(string[] args)
{
Foo myFoo = new Foo();
testFunction(myFoo);
Console.ReadLine();
}
}
In the testFunction, i can make an object called obj2 and set it to obj implicitly without casting it. But why cant i then check the two objects to see if they are the same, without casting it? They obviously implement the same interface, so why is it an error?

You can check to see if they're the same object by using Object.ReferenceEquals or Object.Equals.
However, since your constraints (IA and IB interfaces) don't enforce that the type is necessarily a reference type, there's no guarantee that the equality operator can be used.

Suppose you construct T with a value type X that implements IA.
What does
static void testFunction<T>(T obj) where T : IA
{
IA obj2 = obj;
if (obj == obj2) //ERROR
do when called as testFunction<X>(new X(whatever)) ?
T is X, X implements IA, so the implicit conversion boxes obj to obj2.
The equality operator is now comparing a value type X with a boxed copy of compile-time type IA. That the runtime type is a boxed X the compiler does not care about; that information is ignored.
What comparison semantics should it use?
It cannot use reference comparison semantics because that would mean that obj would also have to be boxed. It won't box to the same reference, so this would always be false, which seems bad.
It cannot use value comparison semantics because the compiler has no basis upon which kind of value semantics it should use! At compile time it does not know whether the type chosen for T in the future will provide an overloaded == operator or not, and even if it does, that operator is unlikely to take an IA as one of its operands.
There are no equality semantics that the compiler can reasonably choose, and therefore this is illegal.
Now, if you constrain T to be a reference type then the first objection goes away, and the compiler can reasonably choose reference equality. If that's your intention then constrain T to be a reference type.

To expand a bit on Reed's answer (which is certainly correct):
Note that the following code results in the same error at compile time:
Guid g = Guid.NewGuid(); // a value type
object o = g;
if (o == g) // ERROR
{
}
The C# language specification says (§7.10.6):
The predefined reference type equality operators are:
bool operator ==(object x, object y);
bool operator !=(object x, object y);
[...]
The predefined reference type equality operators require one of the following:
Both operands are a value of a type known to be a reference-type or the literal null.
Furthermore, an explicit reference conversion (§6.2.4) exists from the type of either operand to the type of the other operand.
One operand is a value of type T where T is a type-parameter and the other operand is the literal null. Furthermore T does not have the value type constraint.
[...]
Unless one of these conditions are true, a binding-time error occurs. Notable implications of these rules are:
[...]
The predefined reference type equality operators do not permit value type operands to be compared. Therefore, unless a struct type declares its own equality operators, it is not possible to compare values of that struct type.
The predefined reference type equality operators never cause boxing operations to occur for their operands. It would be meaningless to perform such boxing operations, since references to the newly allocated boxed instances would necessarily differ from all other references.
Now, in your code example you do not constrain T to be a reference type and hence you get the compile-time error. Your sample can be fixed however by declaring that T must be a reference type:
static void testFunction<T>(T obj) where T : class, IA, IB
{
IA obj2 = obj;
if (obj == obj2) // compiles fine
{
}
}

Try
if (obj.Equals(obj2))
IA doesn't implement any == operator.

Ahh thanks Reed Copsey for that note.
I just also found out that you can put "class" in the where clause like this.
static void testFunction<T>(T obj) where T : class, IA, IB
{
IA obj2 = obj;
if (obj == obj2)
{
}
}
Now its a reference type and it works! :-)

Related

C# Equality check for generic wrapper with implicit cast doesn't work for classes [duplicate]

Some code for context:
class a
{
}
class b
{
public a a{get;set;}
public static implicit operator a(b b)
{
return b.a;
}
}
a a=null;
b b=null;
a = b;
//compiler: cannot apply operator '==' to operands of type tralala...
bool c = a == b;
Is it possible to use == operator on different type instances, where one can implicitly convert to another? What did i miss?
Edit:
If types must be the same calling ==, then why
int a=1;
double b=1;
bool c=a==b;
works?
The implicit operator only works for assignment.
You want to overload the equality (==) operator, as such:
class a
{
public static bool operator ==(a x, b y)
{
return x == y.a;
}
public static bool operator !=(a x, b y)
{
return !(x == y);
}
}
class b
{
public a a{get;set;}
public static implicit operator a(b b)
{
return b.a;
}
}
This should then allow you to compare two objects of type a and b as suggested in your post.
var x = new a();
var y = new b();
bool c = (x == y); // compiles
Note:
I recommmend simply overriding the GetHashCode and Equals method, as the compiler warns, but as you seem to want to supress them, you can do that as follows.
Change your class declaration of a to:
#pragma warning disable 0660, 0661
class a
#pragma warning restore 0660, 0661
{
// ...
}
Is it possible to use == operator on
different type instances, where one
can implicitly convert to another?
Yes.
What did i miss?
Here's the relevant portion of the specification. You missed the highlighted word.
The predefined reference type equality
operators require [that] both operands
are reference-type values or the
literal null. Furthermore, a standard
implicit conversion exists from the
type of either operand to the type of
the other operand.
A user-defined conversion is by definition not a standard conversion. These are reference types. Therefore, the predefined reference type equality operator is not a candidate.
If types must be the same calling ==,
then why [double == int] works?
Your supposition that the types must be the same is incorrect. There is a standard implicit conversion from int to double and there is an equality operator that takes two doubles, so this works.
I think you also missed this bit:
It is a compile-time error to use the
predefined reference type equality
operators to compare two references
that are known to be different at
compile-time. For example, if the
compile-time types of the operands are
two class types A and B, and if
neither A nor B derives from the
other, then it would be impossible for
the two operands to reference the same
object. Thus, the operation is
considered a compile-time error.
I would imagine that you need to actually override the == operator for the types you are interested in. Whether the compile/runtime will still complain even if the types are implicity convertable is something you'll have to experiment with.
public static bool operator ==(a a, b b)
{
//Need this check or we can't do obj == null in our Equals implementation
if (((Object)a) == null)
{
return false;
}
else
{
return a.Equals(b);
}
}
Alternatively just use Equals implementations like ole6ka suggests and ensure that the implementation does the type casting you need.
http://msdn.microsoft.com/en-us/library/8edha89s.aspx
In each case, one parameter must be
the same type as the class or struct
that declares the operator (...)
Use this
bool c = a.Equals(b);

Comparing structs in a generic method

Here's a simplified case of what I'm struggling with:
public bool CompareStruct<S>(S a, S b) where S : struct
{
return a == b;
}
The above will not compile with the error Operator '==' cannot be applied to operands of type 'S' and 'S'.
Normally, I wouldn't be surprised. But I've indicated that S is a struct - so why can't I compare the two parameters?
I don't think this SO question has any relevancy here - after all, I'm working with struct types, not reference types.
The problem here is that the default behavior of == in C# is reference equality. Reference equality in structs makes no sense because it will always return false.
The compiler has no way of knowing if == has been overloaded and S has value equality semantics and therefore disallows its use.
To get around this, use Equals and consider constraining S to IEquatable<S> if appropiate to avoid unnecessary boxing operations.
The problem is that when you specify the constraint that the generic type parameter is struct i.e. ValueType, it is not necessary that the struct which calls this method has provided the overload implementation for == and != operator for it, as for custom value types, when we define it we need to provide the == and != operator overloads for them to be used.
An alternate can be to use Object.Equals method or call the Equals() method on it's own instance like:
public bool CompareStruct<S>(S a, S b) where S : struct
{
return a.Equals(b);
}
or:
public bool CompareStruct<S>(S a, S b) where S : struct
{
return Object.Equals(a,b);
}
The point to remember is that but the equality operator is by default not available for value types unless you overload the == operator for that type and for reference types using == operator does is checking for reference equality so that's why applying constraint to class works fine.
I once wrote a post about this which might be helpful which can be read at this link (Equality Operator (==) and Value Types in C#)
You can't use == on user-defined ValueTypes unless you explicitly override the == and != operators. This is because the default implementation for a struct doesn't implement these operators. For instance, the below doesn't compile:
struct Foo
{
}
void Main()
{
Foo f1;
Foo f2;
if(f1 == f2) // The compiler complains here
{
}
}
So if you can't do that for known structs (by default), then you can't do that for generic structs (less information known at compile time) unless you provide more information (for example that this struct have to implement IEquatable<S>)
The above would work for classes because they uses reference equality by default when using ==. That doesn't apply to Value Types because they are copied by value.

Why is Explicit Operator Not Invoked In Generic Method

I've distilled this question down to the simplest code sample I can think of. Why is the explicit operator not invoked from the generic method?
class Program
{
static void Main(string[] args)
{
A a = new A();
B b = (B)a; // works as expected
b = Cast<B>(a);
}
static TResult Cast<TResult>(object o)
{
return (TResult)o; // throws Invalid Cast Exception
}
}
class A
{
}
class B
{
public static explicit operator B(A a)
{
return new B();
}
}
Because a generic method has one set of IL, based around TResult. It doesn't do different IL per-caller. And the generic TResult does not have any operators.
Also: the operator here would need to be between object and TResult, and you can't define operators involving object.
For example: Cast<int> and Cast<string> and Cast<Guid> all have the exact same IL.
You can cheat with dynamic:
return (TResult)(dynamic)o;
What it comes down to is that the implicit and explicit operators aren't true conversion operators; they're entirely compile time syntactic sugar. Once the code is compiled nothing about the conversion operators remains.
When the compiler sees:
B b = (B) new A();
It says, "is there any native conversion (implicit or explicit) from an A to a B?" (This would be the case if A extended B, for example), or for one of the few special cased language conversions such as double to int (implicit) or int to double (explicit).)
If not, it then looks for user defined conversion operators (it only looks in the definition of A and B, it doesn't look in the definition of C for an implicit conversion from A and to B, for example). If it finds one, then it injects that operator as a static method call, so the code ends up looking like:
B b = B.SomeAutogneratedName(new A());
That way by the time you get to runtime it's just executing another method, something the runtime knows how to do. The only actual runtime conversion operators that are allowed are the handful baked into the language (i.e. from any base type to a parent type, and between certain primitive types).

I can only cast a contravariant delegate with "as"

I'm trying to cast a contravariant delegate but for some reason I can only do it using the "as" operator.
interface MyInterface { }
delegate void MyFuncType<in InType>(InType input);
class MyClass<T> where T : MyInterface
{
public void callDelegate(MyFuncType<MyInterface> func)
{
MyFuncType<T> castFunc1 = (MyFuncType <T>) func; //Error
MyFuncType<T> castFunc2 = func as MyFuncType<T>;
MyFuncType<T> castFunc3 = func is MyFuncType<T> ? (MyFuncType<T>)func : (MyFuncType<T>)null; //Error
}
}
castFunc2 works fine but castFunc1 and castFunc3 cause the error:
Cannot convert type 'delegateCovariance.MyFuncType<myNamespace.MyInterface>' to myNamespace.MyFuncType<T>'
The MSDN article on the as operator states that castFunc2 and castFunc3 are "equivalent" so I don't understand how only one of them could cause an error. Another piece of this that is confusing me is that changing MyInterface from an interface to a class gets rid of the error.
Can anyone help me understand what is going on here?
Thanks!
Add a constraint such that T must be a class.
class MyClass<T> where T: class, MyInterface
This gives the compiler enough information to know that T is convertible. You don't need the explicit cast either.
Variance only applies to reference types. T is allowed to be a value type without the constraint which breaks the compilers ability to prove that T is compatible for contravariance.
The reason the second statement works is because as actually can perform a null conversion. For example:
class SomeClass { }
interface SomeInterface { }
static void Main(string[] args)
{
SomeClass foo = null;
SomeInterface bar = foo as SomeInterface;
}
Foo is obviously not directly convertable to SomeInterface, but it still succeeds because a null conversion can still take place. Your MSDN reference may be correct for most scenarios, but the generated IL code is very different which means they are fundamentally different from a technical perspective.
Eric Lippert gave a great explanation of this issue in his recent posts: An "is" operator puzzle, part one, An "is" operator puzzle, part two.
Main rationale behind this behavior is following: "is" (or "as") operators are not the same as a cast.
"as" operator can result non-null result event if corresponding cast would be illegal, and this is especially true when we're dealing with type arguments.
Basically, cast operator in your case means (as Eric said) that "I know that this value is of the given type, even though the compiler does not know that, the compiler should allow it" or "I know that this value is not of the given type; generate special-purpose, type-specific code to convert a value of one type to a value of a different type."
Later case deals with value conversions like double-to-integer conversion and we can ignore this meaning in current context.
And generic type arguments are not logical in the first context neither. If you're dealing with a generic type argument, than why you're not stating this "contract" clearly by using generic type argument?
I'm not 100% sure what you're want to achieve, but you can omit special type from your method and freely use generic argument instead:
class MyClass<T> where T : MyInterface
{
public void callDelegate(Action<T> func)
{
}
}
class MyClass2
{
public void callDelegate<T>(Action<T> func)
where T : MyInterface
{
}
}
Otherwise you should use as operator with check for null instead of type check.
Your class says that T implements MyInterface because MyInterface is not a instance type. Therefore, MyFuncType<T> is not guaranteed to be MyFuncType<MyInterface>. It could be MyFuncType<SomeType> and SomeType : MyInterface, but that wouldn't be the same as SomeOtherType : MyInterface. Make sense?

Structs, Interfaces and Boxing [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it safe for structs to implement interfaces?
Take this code:
interface ISomeInterface
{
public int SomeProperty { get; }
}
struct SomeStruct : ISomeInterface
{
int someValue;
public int SomeProperty { get { return someValue; } }
public SomeStruct(int value)
{
someValue = value;
}
}
and then I do this somewhere:
ISomeInterface someVariable = new SomeStruct(2);
is the SomeStruct boxed in this case?
Jon's point is true, but as a side note there is one slight exception to the rule; generics. If you have where T : ISomeInterface, then this is constrained, and uses a special opcode. This means the interface can be used without boxing. For example:
public static void Foo<T>(T obj) where T : ISomeInterface {
obj.Bar(); // Bar defined on ISomeInterface
}
This does not involve boxing, even for value-type T. However, if (in the same Foo) you do:
ISomeInterface asInterface = obj;
asInterface.Bar();
then that boxes as before. The constrained only applies directly to T.
Yes, it is. Basically whenever you need a reference and you've only got a value type value, the value is boxed.
Here, ISomeInterface is an interface, which is a reference type. Therefore the value of someVariable is always a reference, so the newly created struct value has to be boxed.
I'm adding this to hopefully shed a little more light on the answers offered by Jon and Marc.
Consider this non-generic method:
public static void SetToNull(ref ISomeInterface obj) {
obj = null;
}
Hmm... setting a ref parameter to null. That's only possibly for a reference type, correct? (Well, or for a Nullable<T>; but let's ignore that case to keep things simple.) So the fact that this method compiles tells us that a variable declared to be of some interface type must be treated as a reference type.
The key phrase here is "declared as": consider this attempt to call the above method:
var x = new SomeStruct();
// This line does not compile:
// "Cannot convert from ref SomeStruct to ref ISomeInterface" --
// since x is declared to be of type SomeStruct, it cannot be passed
// to a method that wants a parameter of type ref ISomeInterface.
SetToNull(ref x);
Granted, the reason you can't pass x in the above code to SetToNull is that x would need to be declared as an ISomeInterface for you to be able to pass ref x -- and not because the compiler magically knows that SetToNull includes the line obj = null. But in a way that just reinforces my point: the obj = null line is legal precisely because it would be illegal to pass a variable not declared as an ISomeInterface to the method.
In other words, if a variable is declared as an ISomeInterface, it can be set to null, pure and simple. And that's because interfaces are reference types -- hence, declaring an object as an interface and assigning it to a value type object boxes that value.
Now, on the other hand, consider this hypothetical generic method:
// This method does not compile:
// "Cannot convert null to type parameter 'T' because it could be
// a non-nullable value type. Consider using 'default(T)' instead." --
// since this method could take a variable declared as, e.g., a SomeStruct,
// the compiler cannot assume a null assignment is legal.
public static void SetToNull<T>(ref T obj) where T : ISomeInterface {
obj = null;
}
The MSDN documentation tells us that structs are value, not reference types. They are boxed when converting to/from a variable of type object. But the central question here is: what about a variable of an interface type? Since the interface can also be implemented by a class, then this must be tantamount to converting from a value to a reference type, as Jon Skeet already said, therefore yes boxing would occur. More discussion on an msdn blog.

Categories

Resources