【问题标题】:object is always not null对象始终不为空
【发布时间】:2015-09-23 20:59:12
【问题描述】:

这总是让我发疯,我仍然无法理解它。在下面的代码中,if 语句被编辑器标记为“表达式始终为真”

 public Task ConfigSendMailAsync(IdentityMessage message)
    {

        const string usertoken = "Sent";
        var mailMessage = new MailMessage();
        mailMessage.To.Add(new MailAddress(message.Destination));
        mailMessage.From = new MailAddress("service@tradertoolkit.com");
        mailMessage.Subject = message.Subject;
        mailMessage.Body = message.Body;


        var credentials = new NetworkCredential(
                   ConfigurationManager.AppSettings["mailAccount"],
                   ConfigurationManager.AppSettings["mailPassword"]
                   );

         var mailClient = new SmtpClient {Credentials = credentials};

        ***if (mailClient != null)***
        {
            mailClient.SendAsync(mailMessage, usertoken);
            return Task.FromResult(0);
        }

        else
        {
            //log error
            return Task.FromResult(-1);
        }
    }

它怎么总是正确的,如果传递的消息无效怎么办,如果创建凭据失败怎么办,等等。当然可以使 if 语句为真。我知道如果之前有任何失败,我会得到一个例外。如果我想确保凭证有效,我肯定会对此进行测试。

 if (credentials != null)
        {

        }

但你猜怎么着,显然这也总是正确的。怎么可能,如果这些应用程序设置中没有存储任何内容怎么办?有人能解释一下这实际上是如何工作的以及如何最好地构造这段代码

【问题讨论】:

  • 你直接在上面赋值,因为构造函数不能返回null,它总是not为null。为什么要在那里检查 null ?正如编译器所说,它不可能为空,因此不需要检查。
  • 您正在分配一个值。如果该分配失败,那么会有一些例外。假设该值不为空是公平的。
  • var mailClient = new SmtpClient {Credentials = credentials};。不为mailClient 分配值的唯一方法是,如果有一个异常会破坏所有内容(在这种情况下,您将永远无法到达if 语句)。在您到达if 语句的所有情况下,mailClient 将包含您在上面的赋值语句中实例化的SmtpClient 实例。

标签: c# async-await


【解决方案1】:

创建SmtpClient 对象不会检查凭据的有效性。它只会将它们存储在字段中以供以后使用。构造函数将创建一个新对象,并且该对象不会为空。结论:if 语句始终为true

但是会发生什么:如果凭据不起作用,SendAsync() 方法可能会抛出 SmtpException。您需要捕获异常。

【讨论】:

  • 我最初在 try catch 中拥有它,但编辑器不喜欢 try 块中的 await
  • @dinotom:真的吗? It should work ...
  • 我一小时后到家时加个帖子,马上出发
【解决方案2】:

在您拥有的代码中,mailClient 永远不会为空,因为您的代码只是实例化了它。如果实例化有问题,上面的行就会失败。

 var mailClient = new SmtpClient {Credentials = credentials};  //This line would throw an error if SmtpClient could not be instantiated
 if (mailClient == null)
 {
    throw new Exception("This will never happen.");
 }

仅当您使用某些函数或工厂模式来创建对象实例时,您才需要进行 null 检查,例如

 var mailClient = SmtpHelper.CreateClient(credentials);
 if (mailClient == null)
 {
     throw new Exception("SmtpHelper returned a null client!");
 }

【讨论】:

    【解决方案3】:

    怎么总是正确的,如果传递的消息是 无效,如果创建凭据失败怎么办,等等。 等等

    在这种情况下,可能会引发异常,并且永远无法到达if。你可以相信这个编译器。

    凭据相同:您举了一个无效凭据的示例。它们肯定是无效的,但不是空的。

    你可以让你的 if 返回 false 的唯一方法是如果你吞下构造函数中发生的异常(这将是一个 可怕 的想法,编译器会检测到它而不显示它警告不再)

    例如:

     try
     {
         var mailClient = new SmtpClient {Credentials = credentials};
     }
     catch(Exception)
     {
         // Evil: you swallow your exception, mailClient is null
         // The program continues and god drowns a kitten.
     }
    
     if (mailClient != null)
     {
         mailClient.SendAsync(mailMessage, usertoken);
         return Task.FromResult(0);
     }
    

    【讨论】:

      【解决方案4】:

      mailClient 永远不是null

      你刚刚更新了它。也许该字段/属性为空,这不是您在那里检查的内容。可能出现了严重错误并引发了异常并且从未分配过 mailClient,但是您将不必进行该检查。

      还有this is how you write asynchronous code。另外,这不是 SQL,我们不会通过返回弱类型整数来判断方法是否有效。

      【讨论】:

        【解决方案5】:

        条件(mailClient != null) 只是一个参考检查。

        如果变量mailClient 是一个悬空指针,则此条件将失败。只要你在内存中有一个有效的空间,mailClient 指向那个内存空间,它就不是空的。

        您关心的条件会问 - mailClient 是否指向任何有效的东西? CLR 会发现 - 是的,名为 mailClient 的引用类型变量中的值是有效的。 然后条件将是 true,因为它不为空。

        当您执行var xyz = new Something(); 时,您保留了一块内存,并且该变量现在指向堆中的该内存。即使您没有填充该成员的字段,存储在xyz 中的地址也不再为空,这就是您的条件始终为true 的方式。


        与此相反,假设您有一个声明 Something xyz;。这一切都意味着xyz 可以存储Something 类型对象的地址。但是,这个变量还没有指向内存空间中的任何有效地址,只是一个悬空指针。这将导致您对(xyz != null) 的条件检查失败。

        【讨论】:

          猜你喜欢
          • 2015-06-04
          • 2019-05-27
          • 1970-01-01
          • 2017-08-19
          • 2019-07-28
          • 2019-03-06
          • 2014-01-17
          • 2015-12-27
          • 1970-01-01
          相关资源
          最近更新 更多