【问题标题】:Why did this compile?为什么要编译?
【发布时间】:2015-09-10 20:03:19
【问题描述】:

下面的代码编译没有问题。很明显_dependency 将永远是null,所以不可能以任何方式使用它(除了评估它) - 是吗?为什么编译器没有意识到这一点而失败了?

public class MyClass
{
   private readonly MyDependency _dependency;

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

需要明确的是,我知道上面的代码是错误的代码,这是开发人员的错 - 但任何其他编译时错误也是如此。我原以为编译器会抛出 use of unassigned variable 类型的错误。

为什么要编译?我是否不知道可以使用这样的null 对象的场景?

编辑:

确认一下 - 我希望依靠编译器来检查写得不好的代码 - 我很欣赏,从语法上讲,这绝对没问题。我的问题确实是双重的,是否有一种我不知道的情况可能会使这段代码执行得很好。第二个问题是 - 如果它已经处理了use of unassigned variable 错误,为什么不会检测到这样的问题?有什么区别?

【问题讨论】:

  • MyDependency 是声明为类还是结构?
  • 这会导致运行时错误。属性(与局部变量不同)分配了默认值。所以没有编译时错误use of unassigned variable
  • 假设MyDependecy有一个名为MyMethod的方法,代码没有问题。检查对象是否已初始化不是编译器的工作。这是在运行时完成的,或者使用第三个工具来提示(如 Resharper)。
  • 想象MyMethod 将是扩展方法(当然,它们还没有在旧的 C#1-days 中引入)。编译这个调用MyDependency.MyMethod(null)的代码会被编译,不是吗?
  • resharper 之类的工具可以发现这种情况并发出警告。但这只是警告。这可能是真的,也可能不是。如果编译器在编译时抛出运行时异常,编译器可能会出错。此代码有效的原因可能有很多。多线程、反射、使用非托管库或托管库等......

标签: c# compiler-errors null


【解决方案1】:

因为编译器不会检查这类问题。它在语法上是正确的,而且非常好。空引用问题通常只在运行时检测到,除非您使用可以检测此类问题的代码分析工具。

【讨论】:

  • 感谢您的回答。根据我对 Ben 的评论,编译器至少有 some 功能可以检查执行路径 - 因为我们有 use of unassigned variable 错误 - 我没有看到这种情况下的区别我正在描述的场景。这不是什么大问题 - 我只是好奇为什么编译器没有检测到问题。
  • 未赋值的变量只检查函数体中的局部变量。
  • 从未使用过的变量不是错误,因此不会停止编译,它只是被优化器删除,如果您在项目设置中启用代码分析,那么您将获得更详细的检查不好的做法
  • @JayMee 因为不存在“本地”问题。问题在于整个类,这比通常的编译器错误更难检测到几个数量级。有一套编译器工具可以完成这项工作,即 Code-Contracts,但它的运行速度非常慢。
【解决方案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();

给出结果“真”

【讨论】:

    【解决方案3】:

    首先,代码编辑器辅助工具可能会给您一个警告,指出该字段未分配并且它可能工作错误。

    .

    其次,您无法在编译时测试成员是否通过反射访问。您可以分配该字段,即使它是只读的。您甚至可以在外部初始化只读字段后调用构造函数。考虑以下示例:

    // 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});
    

    【讨论】:

      猜你喜欢
      • 2016-03-20
      • 2010-10-03
      • 2017-08-30
      • 1970-01-01
      • 2014-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-19
      相关资源
      最近更新 更多