首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >这是斯巴达,还是真的?

这是斯巴达,还是真的?
EN

Stack Overflow用户
提问于 2017-05-23 03:44:22
回答 3查看 9.6K关注 0票数 121

以下是一个面试问题。我想出了一个解决方案,但我不确定它为什么有效。

问题:

在不修改Sparta类的情况下,编写一些代码使MakeItReturnFalse返回false

代码语言:javascript
复制
public class Sparta : Place
{
    public bool MakeItReturnFalse()
    {
        return this is Sparta;
    }
}

剧透我的解决方案:(剧透)

public class Place

{

public interface Sparta { }

}

但是为什么MakeItReturnFalse()中的Sparta引用{namespace}.Place.Sparta而不是{namespace}.Sparta

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-05-23 04:02:07

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

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

前几个项目符号-截断和注释-阅读:

如果命名空间或类型名称的形式为II<A1, ..., AK> so K=0,在我们的情况下为

  • 如果K为零并且命名空间或类型名称出现在泛型方法声明内,则没有泛型如果命名空间或类型名称出现在类型声明内,则对于每个实例类型T (§10.3.1),从该类型声明的实例类型开始,接着是每个封闭类或结构声明的实例类型(如果有):
    • 如果K为零,并且T的声明包含名为I的类型参数,则命名空间或类型名称将引用该类型参数。Nope
    • Otherwise,如果命名空间或类型名称出现在类型声明的主体中,并且T 或其任何基类型包含具有名称IK类型参数的嵌套可访问类型,则命名空间或类型名称引用使用给定类型参数构造的类型。Bingo!

  • 如果前面的步骤不成功,则对于每个命名空间N,从出现命名空间或类型名的命名空间开始,继续每个封闭命名空间(如果有),并以全局命名空间结束,将评估以下步骤,直到找到实体为止:
    • 如果K为零,并且IN中命名空间的名称,则...[是的,将会对进行

因此,最后一个要点是,如果第一个项目符号没有找到任何内容,那么将选择Sparta类……但是当基类Place定义了一个接口Sparta时,它在我们考虑Sparta类之前就被发现了。

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

票数 117
EN

Stack Overflow用户

发布于 2017-05-23 04:02:30

当将一个名称解析为它的值时,定义的“贴近性”被用来解决歧义。无论什么定义是“最接近的”,都是所选择的定义。

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

票数 22
EN

Stack Overflow用户

发布于 2017-06-19 18:20:43

问得好!我想为那些每天不做C#的人补充一点更长的解释……因为这个问题很好地提醒了一般的名称解析问题。

以原始代码为例,通过以下方式略微修改:

  • 让我们打印出类型名称,而不是像在原始表达式中那样比较它们(例如,在Place超类中定义接口Athena以说明接口名称解析。
  • 让我们还打印出this的类型名称,因为它绑定在Sparta类中,只是为了让一切变得非常清楚。

代码如下所示:

代码语言:javascript
复制
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对象并调用这三个方法。

代码语言:javascript
复制
public static void Main(string[] args)
    {
        Sparta s = new Sparta();
        s.printTypeOfThis();
        s.printTypeOfSparta();
        s.printTypeOfAthena();
    }
}

我们得到的输出是:

代码语言:javascript
复制
Sparta
Athena
Place+Athena

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

代码语言:javascript
复制
   public class Place {
        public interface Athena { }
        public interface Sparta { } 
    }

然后这个Sparta -接口-将首先可用于名称查找机制,我们代码的输出将更改为:

代码语言:javascript
复制
Sparta
Place+Sparta
Place+Athena

因此,我们通过在超类中定义Sparta接口,有效地处理了MakeItReturnFalse函数定义中的类型比较,超类首先通过名称解析找到超类。

但为什么C#选择在名称解析中优先考虑超类中定义的接口?@JonSkeet知道!如果您阅读了他的答案,您将获得C#中名称解析协议的详细信息。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44120947

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档