No Equals on interfaces

M

Michi Henning

Herfried K. Wagner said:
That's all true but doesn't apply in the case of what you want to do.
It's OK that VB.NET allows implicit conversions like this:

\\\
Dim foo As IFoo
Dim goo As Object = foo
///

The specification allows this type of conversion. Nevertheless, for
VB.NET, 'Object' is not a base type of interface types because
interfaces cannot inherit from class types.

I find it difficult to see how this opinion could be sustained, given that
the specification explicitly states that Object is the ultimate ancestor
of all types.

Just because I can't explicitly write that an interface inherits from Object
(which would be illegal), that does not mean that the interface does not
implicitly inherit from Object. In fact, the language spec requires the
interface to inherit from Object, because Object is the ancestor of all types.

Michi.
 
H

Herfried K. Wagner [MVP]

* "Michi Henning said:
Hmmm... The compiler permits

oref = iref

and the language specification requires that to work. During
the assignment, an implicit conversion from I to Object takes place.

OK, I was thinking of implicit conversion on a variable of an interface
type to 'Object'...

Ooops. Sorry, I should take more sleep...
 
M

Michi Henning

Herfried K. Wagner said:
The documentation is in contradiction to the implementation:

<URL:http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemtypeclassbas
etypetopic.asp>
says:

"
Interfaces inherit from 'Object' and from zero or more base interfaces;
therefore, the base type of an interface is considered to be 'Object'. The
base interfaces can be determined with 'GetInterfaces' or 'FindInterfaces'.
"

Huh? This quote supports exactly what I've been saying all along: an interface
type "is-a" Object. That's how it should be, and that's perfectly sensible.
The compiler simply has a bug, that's all. (BTW -- C#, Java, C++, Python,
and a whole host of other OO languages all behave the same way -- a reference
to an interface (or abstract base class) can be used invoke methods of a base
of the interface. Nothing new here -- this is as old as OO.)

Michi.
 
H

Herfried K. Wagner [MVP]

* "Michi Henning said:
If *all* references support the methods on type object, why is it then
that I cannot say "iref.Equals"? After all, a reference to an I is a reference.

It seems that the language designers have decided not to allow that...
So, in one case, I have to explicitly cast to Object in order to invoke Equals
(the case I showed), but in another case I don't have to cast explicitly
(namely,
when I assign iref to oref). If the compiler stops from doing one, it should
stop
me from doing the other; or it should allow both. Doing it for one case but not
the other is inconsistent and, moreover, violates the words in the language
specification.

I disagree (see other posts).
 
C

Cor Ligthert

Hi guys,

Can you tho make this multiloog between two people to make more readable do
in in one thread.

I think that than maybe more people can benefit from yours discussion.

Cor
 
C

Cor Ligthert

Hi guys,

Can you tho make this multiloog between two people to make more readable do
in in one thread.

I think that than maybe more people can benefit from yours discussion.

Cor
 
H

Herfried K. Wagner [MVP]

* "Michi Henning said:
Huh? This quote supports exactly what I've been saying all along: an interface
type "is-a" Object. That's how it should be, and that's perfectly sensible.
The compiler simply has a bug, that's all.

You snipped the code sample.

Why is the 'BaseType' property 'null' for interfaces in C# too? Why
does this property behave differently from its documentation?

<URL:http://sharedsourcecli.sscli.net/so...=1.1.1.1&content-type=text/vnd.viewcvs-markup>
says:

---
// Returns the base class for a class. If this is an interface or has
// no base class null is returned. Object is the only Type that does not
// have a base class.
/// <include file='doc\Type.uex' path='docs/doc[@for="Type.BaseType"]/*' />
public abstract Type BaseType {
get;
}
---

So, the implementation is different from the documentation, which is
different from the comment quoted above.
(BTW -- C#, Java, C++, Python,
and a whole host of other OO languages all behave the same way -- a reference
to an interface (or abstract base class) can be used invoke methods of a base
of the interface. Nothing new here -- this is as old as OO.)

This doesn't mean that it must be the same for VB.NET. VB was always
different.
 
D

David


As you say, that's the documentation, *not* the specification. And I
assure you, it's not the only place where the two conflict.
The following table shows the standard widening conversions:
...
Any type -> Object
...
It might seem surprising that a conversion from a derived type to one of
its base types is widening.
The justification is that the derived type contains all the members of the
base type, so it qualifies as
an instance of the base type. In the opposite direction, the base type does
not have the members
defined by the derived type.

Widening conversions always succeed and can always be performed implicitly.

So, the language specifcation states that:

You haven't quoted the specification. Check the headings on the web page you posted.

Anyway, this whole thread is descending into rant rather than
discussion, so a few simple hopefully non-controversial points...

This is the way VB.Net works, and there's no indication that the
language designers consider it a flaw. This is also how the 2005 beta
works, and there's no indication that this will change before release.

There's no "inherent" reason for this behavior, meaning that the
compiler has enough information at compile-time to allow iref.Equals
under strict semantics without requiring runtime binding.

Anyway, one last thought. Arguing about language details in VB.Net
tends to be fairly futile, because frankly the language spec isn't up to
snuff. It's more post-facto documentation than an actual specification.
In C# or C++, one can argue about specific details within the spec and
complain that a certain compiler doesn't implement the section
correctly. In VB.Net, the final word tends to belong to the reference
implementation, i.e., the compiler itself, because the language spec
just isn't written with the kind of attention to detail that an ecma or
ansi specification is written with.

Cheers...
 
C

Cor Ligthert

David,

Do you know what is so nice, because it is not in the language specs it can
not be a bug.

What I have to think about this, in my native language (Dutch) are "Dick"
and "Cock" normal names for man. That does not mean that they should look
like those equivalents in the English language.

Just my thought,

Cor
 
H

Herfried K. Wagner [MVP]

* David said:
As you say, that's the documentation, *not* the specification. And I
assure you, it's not the only place where the two conflict.

Full ACK. Some time ago I reported a bug in the language reference for the
single-line 'If...Then...Else' statement
(<URL:http://lab.msdn.microsoft.com/produ...edbackid=cececc8a-ec3a-438b-bf0e-0fe4665bc8de>).
Even such simple language elements are not documented correctly.

So please, if anybody finds a bug, report it.
Anyway, this whole thread is descending into rant rather than
discussion, so a few simple hopefully non-controversial points...

The discussion is very hard, because everybody can find something in
documentation that helps to strengthen his arguments. It's often
unclear what the intention of MSFT was, so I take the current
implementation as more important than the documentation.
This is the way VB.Net works, and there's no indication that the
language designers consider it a flaw. This is also how the 2005 beta
works, and there's no indication that this will change before release.

I think this behavior will never change.
There's no "inherent" reason for this behavior, meaning that the
compiler has enough information at compile-time to allow iref.Equals
under strict semantics without requiring runtime binding.

I fully agree on this point, but I as mentioned previously, this is
certainly in conflict to what VB programmers think about interfaces.
Anyway, one last thought. Arguing about language details in VB.Net
tends to be fairly futile, because frankly the language spec isn't up to
snuff. It's more post-facto documentation than an actual
specification.

That is mostly because the VB.NET programming language is not
standardized. Maybe future standardization will make documentation
better, but AFAIK there are no plans to standardize VB.NET.
In C# or C++, one can argue about specific details within the spec and
complain that a certain compiler doesn't implement the section
correctly. In VB.Net, the final word tends to belong to the reference
implementation, i.e., the compiler itself, because the language spec
just isn't written with the kind of attention to detail that an ecma or
ansi specification is written with.

Yep.
 
M

Michi Henning

Herfried said:
You snipped the code sample.

Why is the 'BaseType' property 'null' for interfaces in C# too? Why does this property behave differently from its
documentation?

Interesting. Your are right: GetType(I).BaseType returns a null reference
in both languages. (Considering that both languages call into the same
library, it's not surprising that the result is the same in both languages.)
But the result itself is surprising.

So, I did a bit more digging and found a few interesting snippets.

- From the C# language spec:

"For purposes of member lookup, a type T is considered to have the following base types:

[...]

- If T is an interface-type, the base types of T are the base interfaces of T
and the class type object.

- If I write

interface I {}

// ...

Type t = typeof(I);
MethodInfo[] mi = t.GetMethods(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.FlattenHierarchy |
BindingFlags.Static);

The returned method array is empty. This is in direct conflict with what is stated in the spec.

- I can write

iref.Equals(null)

and the compiler is perfectly happy to let me do that. In other words, the compiler
allows access to methods on System.Object via an interface reference, and those methods
do exactly what you'd expect them to do, yet reflection claims that those methods do not
exist.

- If I write

I iref = null;
Console.WriteLine(iref is object);

The compiler omits the following warning:

warning CS0183: The given expression is always of the provided ('object') type

The warning is in agreement with the language specification.

However, when I run that program, it prints false! In other words, the compiler says
that iref "is-a" object, but reflection says iref "is-not-a" object. That's rather sick...

Going through this exercise in VB, we get:

- GetMethods() does not return the methods on Object. Considering that both languages use
the same reflection library, that's not really a surprise.

- If I write

Dim iref As I = Nothing
Console.WriteLine(TypeOf iref Is Object)

the output is "False", which is in accordance with the compiler's idea that interface
types do not inherit from Object.

So, in summary, here is what I can see:

- The C# compiler implements the behavior of the C# language specification, but the
behavior of reflection at run time is in conflict with that specification.

- The VB compiler disagrees with its own language specification, as does the behavior
of reflection at run time.

- The VB compiler is schizophrenic: it allows me to write oref = iref (permitting an
implicit widening from an interface type to Object), but it doesn't allow me to
write iref.Equals(). In other words, the compiler applies implicit widening
in one situation, but not in the other. The compiler should at least be consistent.

Now, what would happen if that were fixed? I see two issues:

Issue 1: What happens if VB is changed to permit the expression iref.Equals(...)?

I don't see a problem here:

All VB programs that currently invoke a method on Object via an interface reference
can do so only by either using an explicit cast (CType) to Object, or by assigning
(or passing) the interface reference to an object reference. If the change is
existing programs will continue to work exactly as they do now.

Semantically, it makes sense to allow access to the methods on Object via an interface
reference: at run time, an interface reference can either be Nothing, or point at
an instance. Anything that is instantiated does indeed provide the methods on Object,
so it cannot happen that the compiler would permit a method call to an object that, at
run time, does not provide the method (i.e., the Smalltalk behavior of calling a
non-existent method at run time cannot arise).

Issue 2: What happens if System.Reflection is changed to return the methods on Object
for a reference to an interface?

That one is more hairy, because it is difficult to see the ramifications. It may well
be that existing code gets unpleasant surprises.

Note that the alternative of changing C# to behave like VB is not viable, because that
would definitely break a lot of existing code.

Cheers,

Michi.
 
H

Herfried K. Wagner [MVP]

* Michi Henning said:
You snipped the code sample.
Why is the 'BaseType' property 'null' for interfaces in C# too? Why
does this property behave differently from its
documentation?

Interesting. Your are right: GetType(I).BaseType returns a null reference
in both languages. (Considering that both languages call into the same
library, it's not surprising that the result is the same in both languages.)
But the result itself is surprising.

So, I did a bit more digging and found a few interesting snippets.

- From the C# language spec:

"For purposes of member lookup, a type T is considered to have the following base types:

[...]

- If T is an interface-type, the base types of T are the base interfaces of T
and the class type object.

- If I write

interface I {}

// ...

Type t = typeof(I);
MethodInfo[] mi = t.GetMethods(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.FlattenHierarchy |
BindingFlags.Static);

The returned method array is empty. This is in direct conflict with what is stated in the spec.

- I can write

iref.Equals(null)

and the compiler is perfectly happy to let me do that. In other words, the compiler
allows access to methods on System.Object via an interface reference, and those methods
do exactly what you'd expect them to do, yet reflection claims that those methods do not
exist.

- If I write

I iref = null;
Console.WriteLine(iref is object);

The compiler omits the following warning:

warning CS0183: The given expression is always of the provided ('object') type

The warning is in agreement with the language specification.

However, when I run that program, it prints false! In other words, the compiler says
that iref "is-a" object, but reflection says iref "is-not-a" object. That's rather sick...

Going through this exercise in VB, we get:

- GetMethods() does not return the methods on Object. Considering that both languages use
the same reflection library, that's not really a surprise.

- If I write

Dim iref As I = Nothing
Console.WriteLine(TypeOf iref Is Object)

the output is "False", which is in accordance with the compiler's idea that interface
types do not inherit from Object.

So, in summary, here is what I can see:

- The C# compiler implements the behavior of the C# language specification, but the
behavior of reflection at run time is in conflict with that
specification.
ACK.

- The VB compiler disagrees with its own language specification, as does the behavior
of reflection at run time.
ACK.

- The VB compiler is schizophrenic: it allows me to write oref = iref (permitting an
implicit widening from an interface type to Object), but it doesn't allow me to
write iref.Equals(). In other words, the compiler applies implicit widening
in one situation, but not in the other. The compiler should at least be consistent.

That's what I don't agree with you and what I am very unsure about.
Let's again take a look at this document:

Visual Basic Language Concepts -- Widening and Narrowing Conversions
<URL:http://msdn.microsoft.com/library/en-us/vbcn7/html/vaconwidenarrowconversions.asp>

"
+-----------+---------------------------------------------+
| Data type | Widens to data types |
+===========+=============================================+
| 'Byte' | 'Byte', 'Short', 'Integer', 'Long', |
| | 'Decimal', 'Single', 'Double' |
+-----------+---------------------------------------------+
| ... | ... |
+-----------+---------------------------------------------+
| 'Char' | 'Char', 'String' |
+-----------+---------------------------------------------+
| ... | ... |

[...]
Widening conversions always succeed and can always be performed implicitly.
"

So, why does this code not compile?

\\\

' Does not compile.
Dim c As Char = "a"c
Dim n As Integer = c.Length
///

'Length' is a method of 'String', and 'Char' widens to 'String'.

I think it is a similar problem for interfaces and widening to
'Object'. Widening doesn't take place when calling a method on a
variable of a type where the method is not defined. Widening is defined
for assigning objects to others (using the '=' operator in VB.NET), for
example, which is a widening conversion:

\\\

' Compiles.
Dim c As Char = "a"c
Dim s As String = c
///

Assume there is a type 'A' that widens to 'B', 'C', 'D', ..., 'Z' (there
is no inheritance or implementation taking place between 'A' and 'B',
'A' and 'C', ...

Should I be able to call all methods defined for 'B', 'C', 'D', ..., 'Z'
on a variable of type 'A'? I don't think so. Instead, I need a cast,
or directly assign the variable to a variable of one of the types the
type widens to:

Sample 1:

\\\
Dim i As IFoo = Nothing
Dim o As Object = i ' Compiles, widening conversion.
Dim t As Type = o.GetType() ' OK.
///

vs.

\\\
Dim i As IFoo = Nothing
Dim t As Type = i.GetType() ' Does not compile, no conversion in code.
///

Sample 2:

\\\
Dim c As Char = "a"c
Dim s As String = c ' Compiles, widening conversion.
Dim n As Integer = s.Length ' Compiles.
///

vs.

\\\
Dim c As Char = "a"c
Dim n As Integer = c.Length ' Does not compile, no conversion in code.
///
Now, what would happen if that were fixed? I see two issues:

Issue 1: What happens if VB is changed to permit the expression iref.Equals(...)?

Then it would have to allow the code in the 2nd listing of sample 2
too. Otherwise there would be a new inconsistency. As shown above this
will lead to confusing code (for example, a character /does not/ have a
length, an /interface/ does not have an 'Equals' method, ...).
I don't see a problem here:

All VB programs that currently invoke a method on Object via an interface reference
can do so only by either using an explicit cast (CType) to Object, or by assigning
(or passing) the interface reference to an object reference. If the change is
existing programs will continue to work exactly as they do now.

I am against this change. For reasons, see above.
Semantically, it makes sense to allow access to the methods on Object via an interface
reference: at run time, an interface reference can either be Nothing, or point at
an instance. Anything that is instantiated does indeed provide the methods on Object,
so it cannot happen that the compiler would permit a method call to an object that, at
run time, does not provide the method (i.e., the Smalltalk behavior of calling a
non-existent method at run time cannot arise).

ACK. I agree. But currently I don't see a reason for a change. The
change would affect more than interfaces only and would change the
behavior of the programming language. The proposed change would not be
a change that breaks existing code, but when allowing implicit widening
conversion on variables directly, then code would be much harder to
understand, because nobody knows all widening conversions of type 'A'
without taking a look at the documentation.
Issue 2: What happens if System.Reflection is changed to return the methods on Object
for a reference to an interface?

That one is more hairy, because it is difficult to see the ramifications. It may well
be that existing code gets unpleasant surprises.

I would not change the current implementation, but instead update the
documentation at the relevant places..
Note that the alternative of changing C# to behave like VB is not viable, because that
would definitely break a lot of existing code.

I think that a change of what C# does would (theoretically) be the best
solution, but there is no reason to change what C# is currently doing.
VB.NET sticks to the rules given by the implementation in the .NET
Framework. The documentation is partly out of date, and the C# specs
are based on this documentation.

(Please excuse bugs in this post, it's about 3:00 AM in Austria and I am
very tired!)
 
D

David

Herfried K. Wagner [MVP] wrote:


So, I did a bit more digging and found a few interesting snippets.

- From the C# language spec:

"For purposes of member lookup, a type T is considered to have the following base types:

[...]

- If T is an interface-type, the base types of T are the base interfaces of T
and the class type object.

- If I write

interface I {}

// ...

Type t = typeof(I);
MethodInfo[] mi = t.GetMethods(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.FlattenHierarchy |
BindingFlags.Static);

The returned method array is empty. This is in direct conflict with
what is stated in the spec.

True, but you're mixing apples and oranges here. There's no reason for
a class in the CLR to follow a language spec. Given the fact that the
CLR is supposedly language neutral, there's no way for the reflection
classes to exactly mirror all the possible implicit method lookup rules
of the various languages.
- I can write

iref.Equals(null)

and the compiler is perfectly happy to let me do that. In other words, the compiler
allows access to methods on System.Object via an interface reference, and those methods
do exactly what you'd expect them to do,

Right. That's a C# method lookup, and it follows the rules you stated
above.
yet reflection claims that those methods do not
exist.

C# is not the CLR. There is nothing in the C# spec that specifies that
a Type class should have a GetMethods call, and nothing that governs the
behavior of GetMethods.
- If I write

I iref = null;
Console.WriteLine(iref is object);

The compiler omits the following warning:

warning CS0183: The given expression is always of the provided ('object') type

The warning is in agreement with the language specification.

However, when I run that program, it prints false! In other words,
the compiler says that iref "is-a" object, but reflection says iref
"is-not-a" object. That's rather sick...

The warning's confusing, but the behavior is correct. Because an
implicit conversion exists from iref to object, the is operator is
equivalent to evaluating "i != null". See section 7.9.9 on the is
operator.
Going through this exercise in VB, we get:

- GetMethods() does not return the methods on Object. Considering that both languages use
the same reflection library, that's not really a surprise.

- If I write

Dim iref As I = Nothing
Console.WriteLine(TypeOf iref Is Object)

the output is "False", which is in accordance with the compiler's idea that interface
types do not inherit from Object.

No, "is" works similarly in both languages.

Dim iref as I = New SomeObjectThatImplementsI
Console.WriteLine(TypeOf iref Is Object)

This prints "True". Class inheritance with the "is" operator works the
same way. If the reference points to null/Nothing, the is operator
returns false when comparing to any type.
So, in summary, here is what I can see:

- The C# compiler implements the behavior of the C# language specification, but the
behavior of reflection at run time is in conflict with that specification.

- The VB compiler disagrees with its own language specification, as does the behavior
of reflection at run time.

- The VB compiler is schizophrenic: it allows me to write oref = iref (permitting an
implicit widening from an interface type to Object), but it doesn't allow me to
write iref.Equals(). In other words, the compiler applies implicit widening
in one situation, but not in the other. The compiler should at least be consistent.

You know I agreed with this argument when you first made it, but here
you've worded it differently and IMHO wrongly. The existence of an
implicit widening conversion does not mean that that conversion may also
be used for implicit method lookup. There's an implicit widening
conversion between integer and double which may be applied during
assignment, but that does not mean you can call TryParse on an integer.

Dim i as integer = 5
i.TryParse(..) ' won't compile
Double d= i ' implicit conversion
d.TryParse() ' this is fine

You were on much firmer ground with the narrower argument that this was
a base-type conversion, which is a subset of widening conversions.
Now, what would happen if that were fixed? I see two issues:

Issue 1: What happens if VB is changed to permit the expression iref.Equals(...)?

I don't see a problem here:

I don't see a problem either, but you're ignoring the argument that
everyone else is making. Nobody's argued that the opposite behavior
would cause compilation or runtime problems, but rather they're arguing
that the behavior would be confusing to VB.Net programmers.

I don't agree with that argument, in fact I find it pretty weak, but it
does no good to ignore it.
 
M

Michi Henning

Herfried said:
So, why does this code not compile?

\\\

' Does not compile.
Dim c As Char = "a"c
Dim n As Integer = c.Length
///

'Length' is a method of 'String', and 'Char' widens to 'String'.

I think it is a similar problem for interfaces and widening to
'Object'. Widening doesn't take place when calling a method on a
variable of a type where the method is not defined. Widening is defined
for assigning objects to others (using the '=' operator in VB.NET), for
example, which is a widening conversion:

\\\

' Compiles.
Dim c As Char = "a"c
Dim s As String = c
///

I take your point. As David points out, I should have been more precise
and talk about conversions from reference-to-derived to reference-to-base.
Assume there is a type 'A' that widens to 'B', 'C', 'D', ..., 'Z' (there
is no inheritance or implementation taking place between 'A' and 'B',
'A' and 'C', ...

Should I be able to call all methods defined for 'B', 'C', 'D', ..., 'Z'
on a variable of type 'A'? I don't think so.

I agree with you there, in the general case. However, for conversions from
derived to base, that is, if 'B', C', 'D', ..., 'Z' are all base types of
'A', it should work. (It works like this for Java, C#, and C++, and there
is no problem with that.)
Then it would have to allow the code in the 2nd listing of sample 2
too. Otherwise there would be a new inconsistency. As shown above this
will lead to confusing code (for example, a character /does not/ have a
length, an /interface/ does not have an 'Equals' method, ...).

I agree with you in that implicit conversions should not be applied for every
type, but should be applied for conversion from derived to base. After all, that
is what happens in every other context, such as when I do an assignment or pass
a reference to a derived to a method that expects a reference to a base.
I am against this change. For reasons, see above.

I didn't mean to suggest exactly what you outlined -- sorry for the confusion.
I'm interested in derived-to-base conversions in this context, not in conversions
for other types, such as double -> int or char -> string.
ACK. I agree. But currently I don't see a reason for a change. The
change would affect more than interfaces only and would change the
behavior of the programming language. The proposed change would not be
a change that breaks existing code, but when allowing implicit widening
conversion on variables directly, then code would be much harder to
understand, because nobody knows all widening conversions of type 'A'
without taking a look at the documentation.

Hmmm... I'm not sure whether this is really a concern -- I honestly do not
recall anyone every having complained about this for C#, Java, or C++.
I would not change the current implementation, but instead update the
documentation at the relevant places..

To me, whether making that change would be a good thing is an open question.
I don't have enough info to decide either way.
I think that a change of what C# does would (theoretically) be the best
solution,

Definitely not. It would break quite a lot of existing code (including quite
a lot of my own code). Invoking methods on a base class via an interface
reference is common. If you look at Ice (www.zero.com), you will find that
this is used all over the place.
but there is no reason to change what C# is currently doing.
VB.NET sticks to the rules given by the implementation in the .NET
Framework. The documentation is partly out of date, and the C# specs
are based on this documentation.

I find the inconsistency jarring. Moreover, I don't really see a good argument
for supporting the VB behavior (other than historical precedent). I don't
agree with the idea that implementing the change I suggested would be confusing.
After all, I can do the same thing just fine with a class -- any base method can
be invoked via reference to a derived type. For interfaces, it's no different.

Cheers,

Michi.
 
M

Michi Henning

David said:
True, but you're mixing apples and oranges here. There's no reason for
a class in the CLR to follow a language spec.

Hmmm... I'm not sure I would agree. At least, I find it surprising when
the object model of the language differs from the object model delivered
by reflection.
Given the fact that the
CLR is supposedly language neutral, there's no way for the reflection
classes to exactly mirror all the possible implicit method lookup rules
of the various languages.

I don't think I'd agree with that either. VB .NET was developed to specifically
fit in with the .NET framework, so there would be unifying substrate for
all the .NET languages. Most certainly, I would expect the VB and the C#
behavior to agree, and I find it jarring that it does not.
C# is not the CLR. There is nothing in the C# spec that specifies that
a Type class should have a GetMethods call, and nothing that governs the
behavior of GetMethods.

Agree. But it is certainly surprising if the language's notion of what the
set of available methods is differs from reflection's notion of what that
set is.
The warning's confusing, but the behavior is correct. Because an
implicit conversion exists from iref to object, the is operator is
equivalent to evaluating "i != null". See section 7.9.9 on the is
operator.

Yes, agree. The problem here isn't really with the behavior, but with
the warning, which is certainly incorrect. The more deep question here
is "what should the result of the following expressison be?"

I iref = null;
iref is I;

In other words, if a reference is null at run time, but it's type is
known at compile time, and the compiler knows that the type being
tested for is the same as or a base of the type of the reference,
should the compiler short-circuit the evaluation
at compile time and replace "iref is I" with "true"?

But that really is an academic question that is only peripheral
to the issue of whether I should be able to invoke a base method
via an interface reference.
You know I agreed with this argument when you first made it, but here
you've worded it differently and IMHO wrongly. The existence of an
implicit widening conversion does not mean that that conversion may also
be used for implicit method lookup. There's an implicit widening
conversion between integer and double which may be applied during
assignment, but that does not mean you can call TryParse on an integer.

I agree.
You were on much firmer ground with the narrower argument that this was
a base-type conversion, which is a subset of widening conversions.

Yes, I should have been more precise.
I don't see a problem either, but you're ignoring the argument that
everyone else is making. Nobody's argued that the opposite behavior
would cause compilation or runtime problems, but rather they're arguing
that the behavior would be confusing to VB.Net programmers.

I don't agree with that argument, in fact I find it pretty weak, but it
does no good to ignore it.

Right. The difference in behavior between C# and VB seems gratuitous and,
moreover, seeing that VB was hacked around severely specifically in order
to fit into the .NET framework seems to suggest that the current VB behavior
was unintentional.

Cheers,

Michi.
 
D

David

I don't think I'd agree with that either. VB .NET was developed to specifically
fit in with the .NET framework, so there would be unifying substrate for
all the .NET languages. Most certainly, I would expect the VB and the C#
behavior to agree, and I find it jarring that it does not.

I tend to think of VB.Net as more of a hybrid. Sometimes its behavior
fits the CLR, and sometimes it tries to mirror VB6, and there's a huge
gray area in between where it tries to split the difference.

Agree. But it is certainly surprising if the language's notion of what the
set of available methods is differs from reflection's notion of what that
set is.

In this case, though, I don't really agree. It's basically a question
of usefulness. Like you, it would annoy me if I couldn't call Equals on
an interface reference, and yet it would also annoy me if reflection on
an interface type returned the methods from object.


Yes, agree. The problem here isn't really with the behavior, but with
the warning, which is certainly incorrect.

Or simply incomplete. "The given expression is always of the provided
type, and will therefore return false only on a null reference". That's
a pretty long error message, though.
The more deep question here
is "what should the result of the following expressison be?"

I iref = null;
iref is I;

It should be false, just as it is. There's extremely good reasons for
this, since the most common use of the is operator is to cast the object
in question and then call its methods.

if (iref is I ) {
((iref) I).Foo(); // obviously, this can't be allowed on null
In other words, if a reference is null at run time, but it's type is
known at compile time, and the compiler knows that the type being
tested for is the same as or a base of the type of the reference,
should the compiler short-circuit the evaluation
at compile time and replace "iref is I" with "true"?

No, because it might be false on a null reference. If the compiler
knows it's null at compile-time (you may have meant to include this),
then I'd still let the JIT do it, but that's an optimization question
not a language question.
But that really is an academic question that is only peripheral
to the issue of whether I should be able to invoke a base method
via an interface reference.

Agreed. It's entirely peripheral, since base classes and subclasses work
with the is operator in this same way. This behavior has nothing to do
with interfaces per se.
Right. The difference in behavior between C# and VB seems gratuitous and,
moreover, seeing that VB was hacked around severely specifically in order
to fit into the .NET framework seems to suggest that the current VB behavior
was unintentional.

I don't think it was unintentional. As everyone's pointed out, changing
this behavior wouldn't break any code (none we've thought of anyway) and
yet it's survived past a couple of versions of the compiler now. And
it's not all that obscure. As you note, this is a relatively common
pattern in C# programming, so it's hard to believe the VB compiler team
hasn't come across it.

I found the idea that this behavior is consistent with VB philosophy to
be an interesting one. I'm not sure I buy it, but it's an interesting
thought. An awful lot of VB.Net language decisions were made because
VB6 programmers think a certain way.
 
H

Herfried K. Wagner [MVP]

Michi!

* Michi Henning said:
I take your point. As David points out, I should have been more precise
and talk about conversions from reference-to-derived to
reference-to-base.

OK. When talking about this, it IMO doesn't apply to interfaces too
:), according to VB.NET's and the Framework's implementation.
I agree with you there, in the general case. However, for conversions from
derived to base, that is, if 'B', C', 'D', ..., 'Z' are all base types of
'A', it should work. (It works like this for Java, C#, and C++, and there
is no problem with that.)

That's what's possible. Members defined in a base class are seen as
members of the derived class, so they can be accessed without an
explicit cast. Bit that's IMO not the case for interfaces, because they
do not inherit from 'Object' :).
I agree with you in that implicit conversions should not be applied for every
type, but should be applied for conversion from derived to base. After all, that
is what happens in every other context, such as when I do an assignment or pass
a reference to a derived to a method that expects a reference to a base.

Full ACK.
I didn't mean to suggest exactly what you outlined -- sorry for the confusion.
I'm interested in derived-to-base conversions in this context, not in conversions
for other types, such as double -> int or char -> string.

IMO, for interfaces to 'Object' there is no derived-to-base
relationship, so the members are not accessible.
Hmmm... I'm not sure whether this is really a concern -- I honestly do not
recall anyone every having complained about this for C#, Java, or C++.

Notice that I am not talking about derived-to-base conversion, but about
widening conversions as defined in the Visual Basic Language Concepts
article mentioned previously. In this case, it would be very
unintuitive to be able to access members of the 'String' class on a
'Char' instance.
To me, whether making that change would be a good thing is an open question.
I don't have enough info to decide either way.

I prefer keeping with the existing implementation, because I don't see
any benefits in a change.
Definitely not. It would break quite a lot of existing code (including quite
a lot of my own code).

I said "theoretically".
Invoking methods on a base class via an interface
reference is common. If you look at Ice (www.zero.com), you will find that
this is used all over the place.

It's maybe used in C#. So keep it possible in C#. It's not used in
VB.NET, and there are no disadvantages in the fact that it cannot be
done in VB.NET, so there is no reason for a change in VB.NET.
I find the inconsistency jarring. Moreover, I don't really see a good argument
for supporting the VB behavior (other than historical precedent). I don't
agree with the idea that implementing the change I suggested would be
confusing.

AFAIS, there is no relationship between interfaces and the 'Object'
class. That's my personal opinion based on the current implementation
in the .NET Framework and VB.NET's behavior that complies to this
implementation.

Implementing the change for all widening conversions that are defined
for VB.NET IMO doesn't make much sense because it will lead to
confusion. VB.NET defines more widening conversions than C#
does, for example, you cannot call methods defined in the 'String' class
on a 'char' in C#. So, by allowing to do that, there would be
additional problems introduced in VB.NET, and no benefits at all.
After all, I can do the same thing just fine with a class -- any base method can
be invoked via reference to a derived type. For interfaces, it's no different.

I fully agree, but this does IMO not apply to interfaces because of the
missing derived-to-base relationship.
 
H

Herfried K. Wagner [MVP]

* Michi Henning said:
Hmmm... I'm not sure I would agree. At least, I find it surprising when
the object model of the language differs from the object model delivered
by reflection.

In this particular case that's what's going on for the C# language
specs and implementation. There is nothing bad with this if it doesn't
cause problems. There are no problems due to providing the methods of
the 'Object' class for interface variables. I can understand this
design decision, and I do not say that I don't like it in C#.
I don't think I'd agree with that either. VB .NET was developed to specifically
fit in with the .NET framework, so there would be unifying substrate for
all the .NET languages. Most certainly, I would expect the VB and the C#
behavior to agree, and I find it jarring that it does not.

I don't agree with that. The behavior of language constructs in VB.NET
and C# IMO doesn't need to be the same. I don't request an 1:1 mapping
of VB.NET's language features onto C# language features. I think the
difference between C# and VB.NET is the main argument for the existence
of these two programming languages. Different behavior is good as long
as it does not cause interoperability problems. The way the C# team has
chosen does not cause such problems and thus should not be criticised.

What I see is an outdated documentation for the 'BaseType' property in
the .NET framework, and some other bugs in the documentation. Based on
the quotes posted in the previous messages in this thread, for me, it
seems that the .NET team once wanted to make 'Object' the ultimate base
class, even for interfaces, but then found out that this is not a good
idea because it causes confusion as interfaces cannot inherit from
classes. The implementation in the Framework was updated, but not the
C# language specs and implementation. This is an assumption only.

The only problem I see is a gap between the C# language specs and
implementation and what the implementation of the .NET Framework is
doing. It's the job of the C# team to explain where the language
behaves differently from the .NET Framework, and framework documentation
needs to be fixed to reflect the current implementation.
Agree. But it is certainly surprising if the language's notion of what the
set of available methods is differs from reflection's notion of what that
set is.

I agree, but for me it's only surprising, but I would not consider that
to be a bug.
Yes, agree. The problem here isn't really with the behavior, but with
the warning, which is certainly incorrect. The more deep question here
is "what should the result of the following expressison be?"

I iref = null;
iref is I;

In other words, if a reference is null at run time, but it's type is
known at compile time, and the compiler knows that the type being
tested for is the same as or a base of the type of the reference,
should the compiler short-circuit the evaluation
at compile time and replace "iref is I" with "true"?

'is' returns the type of the object that is referenced by the variable,
not the type of the variable. From the "C# Programmer's Reference":

"
The is operator is used to check whether the run-time type of an object
is compatible with a given type.
"
But that really is an academic question that is only peripheral
to the issue of whether I should be able to invoke a base method
via an interface reference.
ACK.



I agree.

Well, I think this would lead to a separate discussion. If implicit
widening takes place on variables directly, should this "widening" apply
to shared members too? In my post I have chosen the 'Char'/'String'
sample because there we can talk about instance members.
Right. The difference in behavior between C# and VB seems gratuitous and,
moreover, seeing that VB was hacked around severely specifically in order
to fit into the .NET framework seems to suggest that the current VB behavior
was unintentional.

I don't know, but I am not sure if this behavior was
unintentional. Sometimes things look as if they were unintentional, but
there were hours of discussion behind the decision to make them as they
are.
 
J

Jay B. Harlow [MVP - Outlook]

Michi,
Congratulations!

You have just proven that objects implement an Interface. That within .NET
objects are derived from System.Object.

However! you have not proven that an Interface inherits from System.Object.
If anything the other leg of this thread on Reflection reinforces my point.

My point is that within VB.NET (.NET really) interfaces DO NOT inherit from
System.Object. This is not a bug, this is how Interfaces have been defined
in VB.NET.

Further that C# makes it appear that they do as a benefit for developers, C#
can do this because it knows that you need an object to implement the
Interface, not because the interface inherits from System.Object.

Hope this helps
Jay
 
S

scorpion53061

LOL. Thank you I needed a good laugh this morning.

That analogy was priceless.
 

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