为什么这个可以编译通过?

3
以下代码编译没有问题。很明显,_dependency 总是 null,因此无法以任何方式使用它(除了进行评估) - 是吗?为什么编译器没有意识到这一点并失败呢?
public class MyClass
{
   private readonly MyDependency _dependency;

   public MyClass()
   {
      _dependency.MyMethod();
   }
}

明确一下,我知道上面的代码是糟糕的代码,这是开发人员的问题 - 就像任何其他的编译时错误一样。我本以为编译器会抛出“使用未分配变量”的错误。

为什么会编译通过?难道我不知道可以在某种情况下像这样使用一个“null”对象吗?

编辑:

确认一下 - 我并不希望依赖编译器来检查糟糕的代码,语法上讲,它绝对没问题。我的问题实际上有两个方面,一是是否有我不知道的场景可以使得此代码执行良好。第二个问题是 - 如果编译器已经处理了“使用未分配变量”的错误,为什么它不能检测到这样的问题?有什么区别?


1
MyDependency 声明为类还是结构体? - Dmitry
这会导致运行时错误。属性(不像局部变量)有默认值分配。因此,没有编译时错误“使用未分配的变量”。 - M.kazem Akhgary
3
假设 MyDependency 有一个名为 MyMethod 的方法,那么这段代码没有问题。编译器不会检查对象是否被初始化,这是在运行时或使用第三方工具进行提示(如 Resharper)来完成的。 - Caverna
1
想象一下 MyMethod 是一个扩展方法(当然,在旧的C#1时代它们还没有被引入)。编译这段代码,并调用 MyDependency.MyMethod(null) 应该是可以通过编译的,不是吗? - MakePeaceGreatAgain
1
像resharper这样的工具可以找出这种情况并警告您。但那只是警告。它可能是真的,也可能不是。如果编译器在编译时抛出运行时异常,它可能会出现错误。这段代码能够正常工作可能有很多原因,例如多线程、反射、使用非托管库或托管库等等... - M.kazem Akhgary
显示剩余12条评论
3个回答

7

因为编译器不会检查这种问题。它在语法上是正确的,也完全没有问题。空引用问题通常只能在运行时检测,除非您使用代码分析工具来检测此类问题。


感谢您的回答。根据我的评论向Ben,编译器至少有一些功能检查执行路径 - 因为我们有“未分配变量”的错误 - 我没有看到这种情况与我描述的情况有什么不同。这并不是什么大问题 - 我只是好奇为什么编译器没有检测到问题。 - user1017882
未分配的变量仅在函数体中检查局部变量。 - Bauss
一个从未被使用的变量并不是一个错误,因此不会停止编译,它只会被优化器剔除。如果您在项目设置中启用代码分析,则可以获得更详细的检查以避免不良实践。 - MikeT
1
@JayMee,因为没有“本地”问题。问题出在整个类上,这比通常的编译器错误难以检测到多个数量级。有一组编译器工具可以完成这项工作,叫做Code-Contracts,但运行速度极慢。 - Aron

2

因为这并不是严格错误。也许只是一个警告,但绝对不是编译器错误。

例如,您可以这样做:

public class MyClass
{
    private readonly MyDependency _dependency;

    public MyClass()
    {
        _dependency.MyMethod();
    }

    public bool IsDependencyNull()
    {
        return _dependency == null;
    }
}

public class MyDependency { public void MyMethod() { } } 

然后使用它:
var c = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass));
c.IsDependencyNull().Dump();

返回结果:得到结果“True”。

1

首先,代码编辑器可能会给您一个警告,表明该字段未被分配,这可能会导致其工作不正常。

enter image description here.

其次,在编译时无法测试成员是否被反射访问。即使是只读字段,也可以进行赋值。甚至在外部初始化只读字段后,仍然可以调用构造函数。考虑以下示例:

// creating an instance without executing the constructor
MyClass myClass = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass));

// setting the read-only field
myClass.GetType().GetField("_dependency", BindingFlags.NonPublic | BindingFlags.Instance)
    .SetValue(myClass, new MyDependency());

// invoking the constructor on the already existing myClass
ConstructorInfo ctor = typeof(MyClass).GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
MethodInfo mi = typeof(RuntimeMethodHandle).
    GetMethod("InvokeMethod", BindingFlags.NonPublic | BindingFlags.Static);
object signature = ctor.GetType().GetProperty(
    "Signature", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(ctor);
mi.Invoke(null, new []{myClass, null, signature, false});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接