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?

9633 次浏览

但是为什么 MakeItReturnFalse()中的 Sparta是指 {namespace}.Place.Sparta而不是 {namespace}.Sparta呢?

基本上,因为名称查找规则就是这么说的。在 C # 5规范中,相关的命名规则在3.8部分(“命名空间和类型名称”)。

第一组项目符号(截短并加注)如下:

  • 如果名称空间或类型名称的格式为 II<A1, ..., AK> [所以在我们的例子中 K = 0]:
    • 如果 K 为零且名称空间或类型名称出现在泛型方法声明 [没有,没有泛型方法]
    • 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):
      • 如果 K为零,且 T的声明包含名为 I的类型参数,则名称空间或类型名称引用该类型参数。[不]
      • 否则,如果名称空间或类型名称出现在类型声明的主体中,并且 T 或其任何基本类型包含一个嵌套的可访问类型,其名称为 IK类型参数,那么 名称空间或类型名称引用用给定的类型参数构造的类型。[Bingo!]
  • 如果前面的步骤不成功,那么对于每个名称空间 N,从出现名称空间或类型名称的名称空间开始,继续到每个封闭的名称空间(如果有的话) ,并以全局名称空间结束,计算以下步骤,直到找到一个实体:
    • 如果 K为零,而 IN中名称空间的名称,那么... ... [是的,那将会成功]

如果第一个项目符号没有找到任何东西,那么最后一个项目符号就是找到 Sparta class... 但是当基类 Place定义了一个接口 Sparta时,找到了我们认为是 Sparta类的 之前

注意,如果将嵌套类型 Place.Sparta设置为类而不是接口,它仍然会编译并返回 false——但编译器会发出警告,因为它知道 Sparta的实例永远不会是类 Place.Sparta的实例。同样地,如果您将 Place.Sparta保留为接口,但将 Sparta类设置为 sealed,则会得到一个警告,因为没有 Sparta实例可以实现该接口。

当将名称解析为其值时,定义的“紧密性”用于解决歧义。无论什么定义是“最接近”是一个选择。

接口 Sparta在基类中定义。类 Sparta在包含的命名空间中定义。在基类中定义的事物比在同一命名空间中定义的事物“更接近”。

问得好!我想为那些每天不使用 C # 的人添加一个稍微长一点的解释... 因为这个问题是一个很好的提醒,提醒人们一般的名称解析问题。

以原始代码为例,稍作修改如下:

  • 让我们打印出类型名称,而不是像在原始表达式(即 return this is Sparta)中那样进行比较。
  • 让我们在 Place超类中定义接口 Athena,以说明接口名称解析。
  • Let's also print out the type name of this as it is bound in the Sparta class, just to make everything very clear.

The code looks like this:

public class Place {
public interface Athena { }
}


public class Sparta : Place
{
public void printTypeOfThis()
{
Console.WriteLine (this.GetType().Name);
}


public void printTypeOfSparta()
{
Console.WriteLine (typeof(Sparta));
}


public void printTypeOfAthena()
{
Console.WriteLine (typeof(Athena));
}
}

现在我们创建一个 Sparta对象并调用这三个方法。

public static void Main(string[] args)
{
Sparta s = new Sparta();
s.printTypeOfThis();
s.printTypeOfSparta();
s.printTypeOfAthena();
}
}

我们得到的输出是:

Sparta
Athena
Place+Athena

但是,如果我们修改 Place 类并定义接口斯巴达:

   public class Place {
public interface Athena { }
public interface Sparta { }
}

然后是这个 Sparta——接口——首先对名称查找机制可用,代码的输出将变为:

Sparta
Place+Sparta
Place+Athena

因此,我们仅通过在超类中定义斯巴达接口就有效地搞乱了 MakeItReturnFalse函数定义中的类型比较,该接口首先通过名称解析找到。

But why does C# chose to prioritize interfaces defined in the superclass in the name resolution? @JonSkeet knows! And if you read his answer you'll get the details of the name resolution protocol in C#.