Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么C“编译器”可以;见;DLL中未引用的类的静态属性,而不是实例方法?_C#_Compiler Errors_Dependencies_Dependency Management - Fatal编程技术网

C# 为什么C“编译器”可以;见;DLL中未引用的类的静态属性,而不是实例方法?

C# 为什么C“编译器”可以;见;DLL中未引用的类的静态属性,而不是实例方法?,c#,compiler-errors,dependencies,dependency-management,C#,Compiler Errors,Dependencies,Dependency Management,我的问题的前提是,用简单的英语: 名为Foo的库依赖于名为Bar Foo中的类扩展了Bar中的类 Foo定义了只需传递到Bar的属性/方法 应用程序,FooBar,只依赖于Foo 考虑以下示例: class Program { static void Main(string[] args) { Foo foo = Foo.Instance; int id = foo.Id; // Compiler is happy foo.D

我的问题的前提是,用简单的英语:

  • 名为
    Foo
    的库依赖于名为
    Bar
  • Foo中的类扩展了Bar中的类
  • Foo定义了只需传递到Bar的属性/方法
  • 应用程序,
    FooBar
    ,只依赖于Foo
考虑以下示例:

class Program
{
    static void Main(string[] args)
    {
        Foo foo = Foo.Instance;

        int id = foo.Id; // Compiler is happy
        foo.DoWorkOnBar(); // Compiler is not happy
    }
}
Foo的定义如下

public class Foo : Bar
{
    public new static Foo Instance { get => (Foo)Bar.Instance; }

    public new int Id { get => Bar.Id; }

    public void DoWorkOnBar()
    {
        Instance.DoWork();
    }
}
public class Bar
{
    public static Bar Instance { get => new Bar(); }

    public static int Id { get => 5; }

    public void DoWork() { }
}
的定义如下

public class Foo : Bar
{
    public new static Foo Instance { get => (Foo)Bar.Instance; }

    public new int Id { get => Bar.Id; }

    public void DoWorkOnBar()
    {
        Instance.DoWork();
    }
}
public class Bar
{
    public static Bar Instance { get => new Bar(); }

    public static int Id { get => 5; }

    public void DoWork() { }
}
完全难住我的部分是:

没有对
条的引用

  • FooBar
    可以检索
    Bar
    提供的ID(或者至少它可以编译)
  • FooBar
    无法请求Foo完成由
    Bar
foo.DoWorkOnBar()关联的编译器错误

类型“Bar”是在未引用的程序集中定义的。必须添加对程序集“Bar,版本1.0.0.0,区域性=中性,PublicKeyToken=null”的引用

为什么编译器中出现差异?

如果没有
FooBar
添加对
Bar
的引用,我会假设这两个操作都不会编译。首先,请注意
Foo.Id
Foo.doorkonbar
的实现是不相关的;编译器以不同的方式处理
foo.Id
foo.DoWorkOnBar()
,即使实现不访问
Bar

// In class Foo:
public new int Id => 0;
public void DoWorkOnBar() { }
foo.Id
编译成功,但
foo.DoWorkOnBar()
编译不成功的原因是编译器使用不同的逻辑来查找属性和方法

对于
foo.Id
,编译器首先在
foo
中查找名为
Id
的成员。当编译器看到
Foo
有一个名为
Id
的属性时,编译器会停止搜索,而不必查看
栏。编译器可以执行此优化,因为派生类中的属性会隐藏基类中具有相同名称的所有成员,因此
foo.Id
将始终引用
foo.Id
,而不管
Bar
中可能命名为
Id

对于
foo.DoWorkOnBar()
,编译器首先在
foo
中查找名为
DoWorkOnBar
的成员。当编译器看到
Foo
有一个名为
doorkonbar
的方法时,编译器将继续在所有基类中搜索名为
doorkonbar
的方法。编译器之所以这样做是因为(与属性不同)方法可以重载,并且编译器实现重载解析算法的方式与C#规范中描述的方式基本相同:

  • 从“方法组”开始,它由
    Foo
    中声明的
    DoWorkOnBar
    的所有重载及其基类组成
  • 将集合缩小到“候选”方法(基本上是参数与提供的参数兼容的方法)
  • 删除在更派生的类中被候选方法遮挡的任何候选方法
  • 选择其余候选方法中的“最佳”
  • 步骤1触发了向程序集
    栏添加引用的要求

    C#编译器能否以不同的方式实现该算法?根据C#规范:

    上面描述的解析规则的直观效果如下:要定位由方法调用调用的特定方法,请从方法调用指示的类型开始,然后沿着继承链继续,直到找到至少一个适用的、可访问的、非重写的方法声明。然后对在该类型中声明的一组适用的、可访问的、非重写的方法执行类型推断和重载解析,并调用这样选择的方法

    因此,在我看来答案是“是”:从理论上讲,C编译器可以看到
    Foo
    声明了一个适用的
    DoWorkOnBar
    方法,而不必查看
    Bar
    。然而,对于Roslyn编译器来说,这将涉及对编译器的成员查找和重载解析代码的主要重写,考虑到开发人员自己能够很容易地解决此错误,这可能不值得付出努力


    TL;DR-当您调用一个方法时,编译器需要您引用基类程序集,因为这是编译器实现的方式


    ⑨请参阅的LookupMembersInClass方法


    ²请参阅的performMemberLoadResolution方法。

    ,因为该属性是直接在foo上定义的,因此编译器很满意。但当您想要使用Instance.DoWork()并且DoWork是在bar上定义的时,编译器需要知道在哪里可以找到该DoWork方法。因此,它需要参考barBoth
    Id
    DoWorkOnBar
    直接在
    Foo
    上定义,并从
    Bar
    请求信息/工作-我不太明白您在这里所做的区分。也就是说,
    Bar.Id
    没有在
    Foo
    上定义,但是对于检索
    Foo.Id
    是必需的,但是编译器没有对此抱怨。我同意它看起来不正确,特别是因为只有属性访问(因此只有
    Foo
    引用才能成功编译)由于缺少
    程序集,应用程序在运行时失败。在这两种情况下,似乎都需要引用
    Bar
    ,但编译器只是在方法调用案例中标记问题。如果将实现缩减为
    public class Bar{}
    public class Foo,问题是否仍然存在