PHP 中 C# 的空值合并运算符 (??)

Posted

技术标签:

【中文标题】PHP 中 C# 的空值合并运算符 (??)【英文标题】:C#'s null coalescing operator (??) in PHP 【发布时间】:2011-11-08 21:07:46 【问题描述】:

php中是否有类似于C#的??的三元运算符之类的?

C# 中的?? 简洁而简短,但在 PHP 中您必须执行以下操作:

// This is absolutely okay except that $_REQUEST['test'] is kind of redundant.
echo isset($_REQUEST['test'])? $_REQUEST['test'] : 'hi';

// This is perfect! Shorter and cleaner, but only in this situation.
echo null? : 'replacement if empty';

// This line gives error when $_REQUEST['test'] is NOT set.
echo $_REQUEST['test']?: 'hi';

【问题讨论】:

?: 非常接近 ??。事实上,?: 实际上比?? 捕获更多类似空的情况; ?? 专门用于 null!Nullabe<T>.HasValue。您听起来像是在寻找更像 javascript|| 运算符的东西。就像?:,但 JavaScript 不会抱怨引用未定义的键/成员——尽管如果您尝试引用未定义/空的键/成员,它会抛出错误,因此您只能进入一级。跨度> @dpp,你为什么说someres然后改成test 查看第 7 版。我们终于有了。 PHP 7 有这个功能。请查看wiki.php.net/rfc/isset_ternary 如前所述,这将出现在 PHP 7 中。在早期版本中,我认为这是错误抑制运算符的少数有效用例之一,例如echo @$_REQUEST['someres'] ?: 'hi'; 抑制错误。 【参考方案1】:

PHP 7 添加了null coalescing operator:

// Fetches the value of $_GET['user'] and returns 'nobody'
// if it does not exist.
$username = $_GET['user'] ?? 'nobody';
// This is equivalent to:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

您还可以查看编写 PHP 三元运算符 ?: 的简短方法(仅限 PHP >=5.3)

// Example usage for: Short Ternary Operator
$action = $_POST['action'] ?: 'default';

// The above is identical to
$action = $_POST['action'] ? $_POST['action'] : 'default';

您与 C# 的比较是不公平的。 “在 PHP 中你必须做类似的事情” - 在 C# 中,如果你尝试访问不存在的数组/字典项,也会出现运行时错误。

【讨论】:

真的吗?那么,如果我正在访问一个不存在的数组元素呢?我也会得到错误,是的,这是有道理的。 @NullUserException:谢谢,我总是被这类短语卡住:-( 这是一个完全公平的说法。 System.Linq 命名空间为数组等提供了扩展方法TSource Enumerable.ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index)IDictionary<TKey, TValue>本身提供a method for this:bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value)。此方法返回两个值,因为 C# 更明确地区分 null 和未设置。如果您更愿意平等对待null 并取消设置,则可以轻松编写扩展方法。 @Zenexer:问题不在于方法,而在于运算符。感谢您无缘无故地投反对票。 @zerkms 在 Web 开发中,我们经常处理用户输入。甚至 API 也可能生成具有“可变数量的字段”的结构,我们仍然需要与它们集成。毕竟,isset 函数背后是有原因的。我的例子:empty($var) 但是不是很好。我应该写empty($var['key']) 之类的东西(假设确实定义了 $var,但我对'key' 的存在有疑问),但为了简洁起见,它被省略了。【参考方案2】:

?? 在 C# 中是二进制的,而不是三进制的。它在 PHP 7 之前的 PHP 中没有等价物。

【讨论】:

在 PHP 5.3 中相当于 ?:。例如。 test ?: ifNull === test ? test : ifNull。换句话说,?: 在 PHP 中可以是二进制或三进制,因为中间操作数是可选的。 其实C#中的??是三元的。它是三元运算的语法糖。它只是以二进制形式编写的。 @Zenexer:它是一个二元运算符,它不仅仅是语法糖——func() ?? otherfunc() 只调用一次func,而func()!=null ? func() : otherfunc() 调用它两次,所以如果func 有副作用结果完全不同。 @aboveyou00 有趣;我不知道。【参考方案3】:

在 PHP 7 之前,没有。如果您需要涉及isset,则使用的模式是isset($var) ? $var : null。没有包含isset 特征的?: 运算符。

【讨论】:

这基本上使它在大多数情况下都没用。 :// 我还是找到了很多它的用例;它基本上相当于 PHP 的 Javascript 的 || 运算符,非常方便。 :)【参考方案4】:

从 PHP 5.6 起不存在相同的运算符,但您可以创建一个行为相似的函数。

/**
 * Returns the first entry that passes an isset() test.
 *
 * Each entry can either be a single value: $value, or an array-key pair:
 * $array, $key.  If all entries fail isset(), or no entries are passed,
 * then first() will return null.
 *
 * $array must be an array that passes isset() on its own, or it will be
 * treated as a standalone $value.  $key must be a valid array key, or
 * both $array and $key will be treated as standalone $value entries. To
 * be considered a valid key, $key must pass:
 *
 *     is_null($key) || is_string($key) || is_int($key) || is_float($key)
 *         || is_bool($key)
 *
 * If $value is an array, it must be the last entry, the following entry
 * must be a valid array-key pair, or the following entry's $value must
 * not be a valid $key.  Otherwise, $value and the immediately following
 * $value will be treated as an array-key pair's $array and $key,
 * respectfully.  See above for $key validity tests.
 */
function first(/* [(array $array, $key) | $value]... */)

    $count = func_num_args();

    for ($i = 0; $i < $count - 1; $i++)
    
        $arg = func_get_arg($i);

        if (!isset($arg))
        
            continue;
        

        if (is_array($arg))
        
            $key = func_get_arg($i + 1);

            if (is_null($key) || is_string($key) || is_int($key) || is_float($key) || is_bool($key))
            
                if (isset($arg[$key]))
                
                    return $arg[$key];
                

                $i++;
                continue;
            
        

        return $arg;
    

    if ($i < $count)
    
        return func_get_arg($i);
    

    return null;

用法:

$option = first($option_override, $_REQUEST, 'option', $_SESSION, 'option', false);

这将尝试每个变量,直到找到满足 isset() 的变量:

    $option_override $_REQUEST['option'] $_SESSION['option'] false

如果没有 4,则默认为 null

注意:有一个使用引用的更简单的实现,但它具有setting the tested item to null if it doesn't already exist 的副作用。这个can be problematic 当数组的大小或真实性很重要时。

【讨论】:

没有回答 OP 的问题,即“这样的运营商是否存在”。此外,@LukLed 的回答(在此之后几个月发布)演示了类似的东西,而无需解构关联数组索引。 @benrifkah LukLed 的答案具有将指定键设置为 null 的负面影响:3v4l.org/9vmFR 如果您引用不存在的键,它将被创建。如果您使用不存在的数组,这尤其成问题:3v4l.org/sMsKD 我看到这个“第一个”函数的奇怪结果。想聊聊吗? chat.***.com/rooms/79985/… @benrifkah 抱歉回复晚了;现在就去那里。【参考方案5】:

我使用函数。显然它不是运算符,但似乎比你的方法更干净:

function isset_or(&$check, $alternate = NULL)

    return (isset($check)) ? $check : $alternate;

用法:

isset_or($_REQUEST['test'],'hi');

【讨论】:

为什么要传递$check var 作为参考? @AxelA.Grazx:我作为参考传递,因为如果它不作为参考发送,$_REQUEST['test'] 值将在函数调用之前计算然后传递给函数。如果$REQUEST 数组没有这个元素,它将抛出Undefined index: test 错误。我想避免这种情况。 哦……现在我明白了……不错的方法。 这个答案应该更高——我一直在使用相同的方法,而且非常简洁。 这具有将选中键设置为 null 的负面影响:3v4l.org/9vmFR 这是一个特别成问题的情况:3v4l.org/sMsKD【参考方案6】:

Null Coalesce Operator, (??) 已在PHP 7 中被接受和实施。它与short ternary operator (?:) 的不同之处在于?? 将抑制E_NOTICE,否则在尝试访问没有密钥的数组时会发生这种情况。 RFC 中的第一个示例给出:

$username = $_GET['user'] ?? 'nobody';
// equivalent to: $username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

注意?? 运算符不需要手动应用isset 来防止E_NOTICE

【讨论】:

它实际上可以在 isset() 的任何地方工作,因此它也适用于变量和属性。 另外,一个不错的功能是您可以链接它,例如:$_GET['user'] ?? $_SESSION['user'] ?? 'bob'; - 当它短路时,您甚至可以进行函数调用。另外,不像isset($foo) ? $foo : $bar,如果它是一个函数调用(例如foo()[0]),它不会计算两次。 :) @AndreaFaulds 你应该说,不像普通的三元,你可以不用traumatic brain injury链接它。 嗯,你也可以使用短三元运算符?: 来做到这一点,这是一个不同的用例。

以上是关于PHP 中 C# 的空值合并运算符 (??)的主要内容,如果未能解决你的问题,请参考以下文章

是啥 ?。运营商和我可以在哪里使用? JavaScript中的空值合并运算符[重复]

C#,多个 == 运算符重载,没有模棱两可的空值检查

F#中的空合并运算符?

C#中泛型参数的空值或默认比较

空值合并运算符 angular 2

为啥 PHP 的空合并运算符 (??) 不能处理具有不同可见性的类常量?