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的属性/方法
- 应用程序,
,只依赖于FooFooBar
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
提供的ID(或者至少它可以编译)Bar
无法请求Foo完成由FooBar
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方法。因此,它需要参考barBothId
和DoWorkOnBar
直接在Foo
上定义,并从Bar
请求信息/工作-我不太明白您在这里所做的区分。也就是说,Bar.Id
没有在Foo
上定义,但是对于检索Foo.Id
是必需的,但是编译器没有对此抱怨。我同意它看起来不正确,特别是因为只有属性访问(因此只有Foo
引用才能成功编译)由于缺少条
程序集,应用程序在运行时失败。在这两种情况下,似乎都需要引用Bar
,但编译器只是在方法调用案例中标记问题。如果将实现缩减为public class Bar{}
和public class Foo,问题是否仍然存在