Why does vb.net reject assignment to nested covari

2019-06-21 20:09发布

问题:

In the code:

Interface ISelf(Of Out TMe)
End Interface
Class SomeBase
    Implements ISelf(Of SomeBase)
End Class
Class SomeDerived
    Inherits SomeBase
    Implements ISelf(Of SomeDerived)
End Class
Module ISelfTester
    Sub TestISelf()
        Dim z7 As New SomeDerived
        Dim z8 As ISelf(Of SomeDerived)
        Dim z9 As ISelf(Of ISelf(Of SomeDerived))
        z8 = z7
        z9 = z8
        z9 = z7 ' Why is this illegal?
    End Sub
End Module

The assignment directly from Z7 to Z9 yields the message "Error 13 Option Strict On does not allow implicit conversions from 'wokka.SomeDerived' to 'wokka.ISelf(Of wokka.ISelf(Of wokka.SomeDerived))' because the conversion is ambiguous." How is that assignment any more ambiguous than the one from Z7 to Z8, or Z8 to Z9? So far as I can tell, all three assignments must be representation-preserving conversions, meaning that all three must simply store a reference to the same object as Z7.

I could understand that if I were trying to assign an instance of SomeDerived to a reference of type ISelf(Of ISelf(Of SomeBase)), attempting to access a member of that interface could yield the implementation from either SomeBase or SomeDerived; if the member was a method with return type TMe, I could understand that such ambiguity could cause compilation to fail (since the compiler wouldn't know what the return type would be). I'm puzzled, though, as to why merely trying to assign a reference fails because of "ambiguity", given that the assignment can't possibly be interpreted as anything other than a direct store of a reference to a reference-type variable?

BTW, the intended usage would be for ISelf(Of T) to contain a read-only property Self of type T, for which the expected implementation would be Return This [a representation-preserving conversion in every case; I suppose I should have added a class constraint to TMe, but it doesn't affect the original problem]. If has a variety of classes which one is interested in implement ISelf(Of theirOwnTypes), it should be possible to leverage the covariance of ISelf to facilitate some things that would otherwise be difficult [e.g. if each class one is interested in that implements IMoe, ILarry, and/or ICurly, etc. also implements the corresponding classes ISelfAndMoe(Of ItsOwnType), ISelfAndLarry(Of ItsOwnType), and/or ISelfAndCurly(Of ItsOwnType), etc. then one can accept a parameter type which is known to implement any combination of those interfaces e.g.ISelfAndMoe(Of ISelfAndLarry(Of ICurly)) param. Given that declaration,paramwould implementIMoe, andparam.Selfwould implementILarry, andparam.Self.Selfwould implementICurly. Further, if the class implements the expected pattern, one could castparamto e.g.ISelfAndCurly(Of IMoe), if one wanted to call a routine which needed those two interfaces but didn't needILarry` (such cast could fail if an implementation did something unexpected, but should succeed if the object's class follows the expected pattern).

回答1:

Option Strict On tells the compiler to ignore a simple reference to reference implicit conversion. It's implicit because z9 is allowed to be a SomeBase(Of SomeBase(Of SomeDerived) and because SomeDerived can be substituted for SomeBase the compiler isn't sure which one you mean. But because you explicitly say in z8 that it's ISelf(of SomeDerived) there isn't any guesswork. I suspect if you changed z9 to ISelf(Of SomeDerived(Of SomeDerived) that ambiguity may go away.

As an opinion, however, this sort of nesting gets very confusing as it stacks on itself and can become a nightmare to maintain.



回答2:

I also find this hard to understand. Had it been instead:

Dim y9 As ISelf(Of ISelf(Of SomeBase))
y9 = z7

it would be more easy to imagine many "routes" from SomeDerived to ISelf(Of ISelf(Of SomeBase)).

Looking at the example of the question, clearly:

SomeDerived "is a" ISelf(Of SomeDerived)

by declaration. From here, using covariance, one gets to:

ISelf(Of SomeDerived) "is a" ISelf(Of
                                      ISelf(Of SomeDerived)
                                                           )

But are there more than one way to get from the first "equation" to this one? One way seems to be to insert the first equation into itself, using covariance of course. Another way might be to apple ISelf(Of ...) on both sides of the "is a" in the first equation, and then use some kind of transitivity to combine the original equation with the result.

I'm really not sure if those are distinct ways. Is there some kind of associativity here, as in the abstract (formal) mathematical equation:

(a·a)*x  =  a*(a*x)

Maybe I don't make sense, and this is just an error in the VB.NET compiler. What happens if you delete the base class SomeBase from the example? Does this behavior still occur?

There's another question on errors with contravariance where it's claimed that VB.NET is overly strict. But maybe it's better to be too strict than too loose like C# seems to be.