The following is an interview question. I came up with a solution, but I'm not sure why it works.
Question:
Without modifying the Sparta
class, write some code that makes MakeItReturnFalse
return false
.
public class Sparta : Place
{
public bool MakeItReturnFalse()
{
return this is Sparta;
}
}
My solution: (SPOILER)
public class Place
{
public interface Sparta { }
}
But why does Sparta
in MakeItReturnFalse()
refer to {namespace}.Place.Sparta
instead of {namespace}.Sparta
?
But why does
Sparta
inMakeItReturnFalse()
refer to{namespace}.Place.Sparta
instead of{namespace}.Sparta
?
Basically, because that's what the name lookup rules say. In the C# 5 specification, the relevant naming rules are in section 3.8 ("Namespace and type names").
The first couple of bullets - truncated and annotated - read:
- If the namespace-or-type-name is of the form
I
or of the formI<A1, ..., AK>
[so K = 0 in our case]:
- If K is zero and the namespace-or-type-name appears within a generic method declaration [nope, no generic methods]
- Otherwise, if the namespace-or-type-name appears within a type declaration, then for each instance type T (ยง10.3.1), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class or struct declaration (if any):
- If
K
is zero and the declaration ofT
includes a type parameter with nameI
, then the namespace-or-type-name refers to that type parameter. [Nope]- Otherwise, if the namespace-or-type-name appears within the body of the type declaration, and
T
or any of its base types contain a nested accessible type having nameI
andK
type parameters, then the namespace-or-type-name refers to that type constructed with the given type arguments. [Bingo!]- If the previous steps were unsuccessful then, for each namespace
N
, starting with the namespace in which the namespace-or-type-name occurs, continuing with each enclosing namespace (if any), and ending with the global namespace, the following steps are evaluated until an entity is located:
- If
K
is zero andI
is the name of a namespace inN
, then... [Yes, that would succeed]
So that final bullet point is what picks up the Sparta
class if the first bullet doesn't find anything... but when the base class Place
defines an interface Sparta
, it gets found before we consider the Sparta
class.
Note that if you make the nested type Place.Sparta
a class rather than an interface, it still compiles and returns false
- but the compiler issues a warning because it knows that an instance of Sparta
will never be an instance of the class Place.Sparta
. Likewise if you keep Place.Sparta
an interface but make the Sparta
class sealed
, you'll get a warning because no Sparta
instance could ever implement the interface.
See more on this question at Stackoverflow