This is Sparta, or is it?

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?

Jon Skeet
people
quotationmark

But why does Sparta in MakeItReturnFalse() 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 form I<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 of T includes a type parameter with name I, 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 name I and K 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 and I is the name of a namespace in N, 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.

people

See more on this question at Stackoverflow