It's my first post here.
Could anyone please explain me the behaviour of the following code?
<code>
interface IFoo<T, P>
{
void Bar(T t, P p); // (1)
void Bar(P p1, P p2); // (2)
}
class NastyFooImpl : IFoo<int, int>
{
void IFoo<int, int>.Bar(int t, int p)
{
Console.WriteLine("void IFoo<int, int>.Bar(int t, int
p)");
}
public void Bar(int p1, int p2)
{
Console.WriteLine("public void Bar(int p1, int p2)");
}
}
class Program
{
static void Test<Foo, T, P> () where Foo : IFoo<T, P>, new()
{
Foo foo = new Foo();
T t = default(T);
P p = default(P);
foo.Bar(t ,p);
foo.Bar(p, p);
}
static void Main(string[] args)
{
Test<NastyFooImpl, int, int>();
}
}
</code>
The output I'm getting using Visual Studio 2005:
<code>
void IFoo<int, int>.Bar(int t, int p)
public void Bar(int p1, int p2)
</code>
When I swap lines (1) and (2), the order of lines of output changes as
well.
Any links are greatly appreciated, especially links to C#
specification.
<snip>
Yowser, that's horrible!
I'll look at the spec closely tonight and see whether it helps.
The fact that (as you point out) the behaviour changes depending on
interface declaration order suggests something very nasty. It feels
like it shouldn't compile, but I wouldn't like to say exactly where ;)
Has this come up as an issue in production code? If so, regardless of
whether it's officially legal or what the compiler should really be
doing, I'd suggest trying to refactor things to avoid the situation -
you really don't want to have whoever reads the code next to have to
worry about it :)
Jon
Overloading is determined at compile time, and for generics note that
the overloads are fixed in *generic* terms, not for specific types;
As such, given "T t" & "P p", the only match on Bar(t,p) is via
IFoo<T,P>.Bar(t, p), and the only match on Bar(p,p) is via
IFoo<T,P>.Bar(p1, p2); because this is decided early for *all* P, T,
there isn't any ambiguity here at all. If, however, you close the
generic sooner (to int, int), then there *would* be an ambiguity:
IFoo<int, int> foo = new NastyFooImpl();
foo.Bar(5,6); // which to call?
I believe the spec mentions a related case in particular, but I can't
find the reference. It was something like:
class SomeType<T> {
this[int index] {}
this[T key] {}
}
then refererring to SomeType<int> by an int indexer. IIRC the "index"
version wins over the "key" version because it checks non-generic
members first.
But as Jon said: yowser! Is this purely academic?
Marc
Marc
But the example of the OP is about interface mapping.
>
> As such, given "T t" & "P p", the only match on Bar(t,p) is via
> IFoo<T,P>.Bar(t, p), and the only match on Bar(p,p) is via
> IFoo<T,P>.Bar(p1, p2); because this is decided early for *all* P, T,
> there isn't any ambiguity here at all. If, however, you close the
> generic sooner (to int, int), then there *would* be an ambiguity:
>
> IFoo<int, int> foo = new NastyFooImpl();
> foo.Bar(5,6); // which to call?
That is ambiguous and throws an compilererror.
None of the two members can be called on an expression of type IFoo<int,
int>.
>
> I believe the spec mentions a related case in particular, but I can't
> find the reference. It was something like:
> class SomeType<T> {
> this[int index] {}
> this[T key] {}
> }
> then refererring to SomeType<int> by an int indexer. IIRC the "index"
> version wins over the "key" version because it checks non-generic
> members first.
No, the call is ambiguous and results in a compiler error.
Christof
Yes, see my retraction; now, I don't claim to be a .Net king, but
I'm not a slouch either - and it threw me off the scent, which to
my mind is a warning sign "don't do this even if it does work" ;-p
[re similar case as cited]
> No, the call is ambiguous and results in a compiler error.
Not so; try this:
public class SomeDictionary<TKey> {
public string this[int index] { get { return "index"; } }
public string this[TKey key] { get { return "key"; } }
}
static class Program
{
static void Main(string[] args)
{
SomeDictionary<int> wossit = new SomeDictionary<int>();
string called = wossit[3]; // returns "index" as claimed
}
}
ref ECMA 334, 3rd edtion; 14.4.2.2 Better function member
"If one of MP and MQ is non-generic, but the other is generic, then
the non-generic is better."
> Yowser, that's horrible!
I agree.
> I'll look at the spec closely tonight and see whether it helps.
Thanks, I'm looking forward to it.
> Has this come up as an issue in production code?
Well I have use-case for instantiating generic interface with equal
types, but I don't really need explicit interface implementation like
in the code snippet I posted. I've encountered this dark corner of the
language by accident. The purpose of this thread is to shed some light
on this issue, regardless of whether tricks like this should be used
in production.
Marc
This are possibly ambiguous members, they should be avoided but are
permitted
>
> class NastyFooImpl : IFoo<int, int>
> {
> void IFoo<int, int>.Bar(int t, int p)
> {
> Console.WriteLine("void IFoo<int, int>.Bar(int t, int
> p)");
> }
>
> public void Bar(int p1, int p2)
> {
> Console.WriteLine("public void Bar(int p1, int p2)");
> }
> }
The question is, wich method implements wich interface member.
The first implementation fits for both. There doesn't seem to be a rule wich
permits one explicit interface implementation to be valid for two or more
member.
The second method also fits for both, but explicit interface implementations
take precedence over implicit interface implementations.
Both interface members should map on the first member.
>
> class Program
> {
> static void Test<Foo, T, P> () where Foo : IFoo<T, P>, new()
> {
> Foo foo = new Foo();
> T t = default(T);
> P p = default(P);
> foo.Bar(t ,p);
> foo.Bar(p, p);
Here the both members are unambiguous. The first statement calls the first
member, the second calls the second member. Wich implementations are called
depends of the interface
> }
> static void Main(string[] args)
> {
> Test<NastyFooImpl, int, int>();
> }
> }
> </code>
>
> The output I'm getting using Visual Studio 2005:
> <code>
> void IFoo<int, int>.Bar(int t, int p)
> public void Bar(int p1, int p2)
> </code>
This would be a bug, since both should call the explicit implementation.
>
> When I swap lines (1) and (2), the order of lines of output changes as
> well.
Also a bug.
Christof
Actually, your conclusion is right. But it's the wrong rule.
A generic member is a member that has itself type parameters, not a method
that has a type parameter as a type of its parameters.
Applicable is the rule of more specific parameter types:
"Otherwise, .... if one method has more specific parameter types, then that
method is better. .....
A type parameter is less specific than a non-type parameter. ...."
Christof
But equally, I can't find one which expressly denies this (it may be
there,
but if so I can't see it). Likewise, there is no syntax for explicit
disambiguation. Prior to generics it was a non-issue as you
couldn't duplicate the signature within a single interface (the spec
[as
we all know] allows you to disambiguate between signature-equivalent
members on different interfaces).
A specification failure, perhaps then...?
Marc
Fair enough; good catch.
Marc
Oops, that's what I wanted to say, "wich forbids" not "wich permits".
Christof
<snip>
> When I swap lines (1) and (2), the order of lines of output changes as
> well.
> Any links are greatly appreciated, especially links to C#
> specification.
Leaving the spec itself aside, I'm not sure this isn't actually a CLR
issue. The IL generated for the calls themselves is the same whichever
way round the interface is declared:
callvirt instance void class IFoo`2<!!T,!!P>::Bar(!0, !1)
...
callvirt instance void class IFoo`2<!!T,!!P>::Bar(!1, !1)
So I think it's the CLR resolution which is causing the massive oddity.
I still haven't looked in detail at what the spec says.
I'll mail Eric Lippert later on to see what he makes of it.
--
Jon Skeet - <sk...@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
<snip>
The C# 3.0 compiler can explain it for you, now I've tried it:
Test.cs(11,10): warning CS0473: Explicit interface implementation
'IFoo<...>.Bar' matches more than one interface member. Which
interface member is actually chosen is implementation-dependent.
Consider using a non-explicit implementation instead.
So at least in the future you'll be warned about such things.
I like the 3.0 warning, though ;-p
It's not the calls that have unusual behavior, that's well defined. It's
the override behavior during implementation.
What exactly do you mean? I'm afraid I didn't fully follow either
sentence. Note that the C# 3.0 compiler warns that the behaviour
*isn't* specified.
Are you suggesting that the CLR is buggy but that the C# specification
*does* say which version should be called, or that the C# compiler is
definitely generating the right calls, but how those calls are resolved
is undefined?
But that is the call of the interface members. Each of them choses the right
interface member. The problem is, how the interface member is maped to the
methods.
The calling occurs on a constructed class, where the two overloads can
clearly ditinguished.
The interface mapping for NastyFooImpl occurs on a constructed type, where
the members can't be distinguished. We'de have to look how the interface
mapping is defined in the IL to see, if the C# compiler or the CLR makes the
decission.
Christof
Still, from the specs, the implementation should map an both members. So
it's not implementation defined. Also, the distinction can't be made by
using non-explicit implementation.
But maybe, there is a change in the interface mapping rule between C#2.0 and
C#3.0
Christof
<snip>
> But that is the call of the interface members. Each of them choses the right
> interface member. The problem is, how the interface member is maped to the
> methods.
Agreed.
> The calling occurs on a constructed class, where the two overloads can
> clearly ditinguished.
> The interface mapping for NastyFooImpl occurs on a constructed type, where
> the members can't be distinguished. We'de have to look how the interface
> mapping is defined in the IL to see, if the C# compiler or the CLR makes the
> decission.
I've made what I think is a fairly important discovery, in terms of
the difference that is made by reordering the interface members. One
way round, the IL for the explicit implementation is:
.override method instance void class IFoo`2<int32,int32>::Bar(!
1, !1)
The other way round, it's
.override method instance void class IFoo`2<int32,int32>::Bar(!
0, !1)
In other words it's explicitly implementing different methods, which
explains everything else, I think. That would put the ball in the C#
compiler's court (rather than the CLR's) but it doesn't answer whether
it's a C# compiler *bug* or a problem with the spec.
Jon