为什么在C#中1 && 2是false?

14

我对我的另一个问题感到沮丧。因此,我写了这个例子。

在C语言中,以下内容是正确的。参见演示

int main()
{
printf("%d", 1 && 2);
return 0;
}

输出:

1

在C#中,它是假的。为什么是假的?另外,我不明白为什么在这个例子中需要创建布尔运算符,而在我的另一个问题中不需要,但无论如何,为什么下面的代码是假的?对我来说没有意义。
顺便说一下,使下面的代码变为假的逻辑在这里描述。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MyInt a=1, b=2;
            bool res=a && b;
            Console.WriteLine("result is {0}", res);
        }

        class MyInt
        {
            public int val;
            public static bool operator true(MyInt t) { return t.val != 0; }
            public static bool operator false(MyInt t) { return t.val == 0; }
            public static MyInt operator &(MyInt l, MyInt r) { return l.val & r.val; }
            public static MyInt operator |(MyInt l, MyInt r) { return l.val | r.val; }
            public static implicit operator MyInt(int v) { return new MyInt() { val = v }; }
            public static implicit operator bool(MyInt t) { return t.val != 0; }
        }
    }
}

12
如果你想使用C语言编写代码,那就用C语言。如果你想用C++语言编写代码,那就用C++语言。如果你想用C#语言编写代码,那就用C#语言。但是你不能认为这些语言是可以互换的。如果你按照所编写语言的规则编写代码,而不是按照其他语言的规则编写代码,那么你将会获得最大的成功。 - David Heffernan
4
常见约定应该始终优先于其他方面——如果1一直为真,0一直为假,那么做事方式不同的语言应该受到批评。我们这些可怜的人只能记住那么多,而且我们会犯错误,所以改变约定只会使错误更加可能发生。 - gbjbaanb
2
@gbjbaanb:确切地说,如果它要改变,至少告诉我们背后的原因,这样我们就可以利用它为什么会改变。这就是我一直在尝试询问的(三次。几乎没有人明白)。这(为什么)就是我喜欢CodeInChaos在另一个问题中的回答的原因https://dev59.com/RlTTa4cB1Zd3GeqPw-2r#5203892。 - user34537
@acidzombie24:当你学习一门新语言时,建议先阅读该语言的规范说明。我并不是要泼冷水……这是一个严肃的建议。语言会随着时间的推移而演变,了解你已经掌握的知识和正在学习的知识之间的区别非常重要。 - Eric J.
1
@Eric J.:我读过泛型、getter、setter、this[T t]、显式/隐式转换的工作原理,因为它与C++中的operator T()不同。然而,我没有研究过for循环或者'|'是如何工作的,因为每种语言都有它们,并且它们都是相同的。但是,我从来没有想到||会有所不同,直到现在才看到了不同之处。它做得很好,让我相信它是相同的。我不认为你建议我应该阅读每种语言中函数调用的工作原理吧?(但我知道命名参数,但那是自从我开始使用的版本以来新出现的)。或者赋值运算符呢? - user34537
显示剩余3条评论
4个回答

22
在C语言中没有bool类型。通常认为0是false,!= 0是true。if语句以此方式处理条件表达式结果。
在C++中,引入了bool类型,但它与旧规则兼容,0作为false,false作为0,并且int和bool之间存在隐式转换。
在C#中,情况并非如此:有bool和int类型,它们不能相互转换。这就是C#标准所说的。所以,当你试图重新实现bool和int的兼容性时,你犯了一个错误。你使用&&,这是逻辑运算符,但在C#中你不能重载它,只能用按位运算符&,其实现方式是按位的。1 & 2 == 0 == false!就是这样!
你甚至不应该重载按位运算符,为了保持兼容性,你只需保留operator true和false。
这段代码可以按你的期望工作:
class Programx
{
    static void Main(string[] args)
    {
        MyInt a = 1, b = 2;
        bool res = a && b;
        Console.WriteLine("result is {0}", res);
    }

    class MyInt
    {
        public int val;
        public static bool operator true(MyInt t)
        {
            return t.val != 0;
        }
        public static bool operator false(MyInt t)
        {
            return t.val == 0;
        }
        public static implicit operator MyInt(int v)
        {
            return new MyInt() { val = v };
        }
        public static implicit operator bool(MyInt t)
        {
            return t.val != 0;
        }
    }
}

结果为真


2
在C语言中,直到1999年才出现了bool类型。 - Mike Seymour
@acidzombie24 那个答案很棒。 - Andrey
1
即使在今天,处理器仍然使用这些指令。将bool与int分开的抽象不会影响性能,但可以使语言更加清晰。就像对于处理器来说,指针和整数之间没有区别一样。但在语言中将它们分开仍然是有意义的。通常情况下,如果你对某件事情太熟悉了,你就会对其他选择视而不见,即使这个选择更好。不幸的是,这种情况经常发生在我们身上,比我们想象的要多。 - CodesInChaos
1
你不会在运行时浪费任何周期。而同一时代的 Pascal 编译器也具有这个精确特性。 - CodesInChaos
@dmckee:我想知道是否有/曾经有过任何机器,如果结果非零,则具有a && ba || b始终返回1将比在早期退出情况下返回a的值以及否则返回b的值更快?除了在许多情况下更有帮助之外,我知道许多机器都会采用后一种语义。 - supercat
显示剩余10条评论

9
你的operator&和operator|实现是错误的。 当应用于整数类型时,这些二元运算符具有按位含义,而当应用于布尔类型或具有自己的&和|运算符的类时,它们具有逻辑AND和OR语义(是&&和||非短路的近亲)。 正确的实现如下所示:
operator &(MyInt l, MyInt r) {return l.val != 0 && r.val != 0);}
operator |(MyInt l, MyInt r) {return l.val != 0 || r.val != 0);}

为什么会这样呢?!?! - user34537
2
@acidzombie24:恐怕我不理解你所说的区别。当&和|作为用户定义的类运算符得到支持时,它们将同时支持非短路逻辑(即布尔)操作和短路逻辑(即布尔)操作&&和||。它们不用于支持按位(即二进制数学)运算。 - Jollymorphic
你的代码是错误的,因为你混淆了位运算符和逻辑运算符。 - Andrey
4
@acid && 在任何合理类型上都不是一个短路位运算符。你问题的核心在于误解了在C#中 & 的意思是按位 and。事实上,它表示非短路的and - CodesInChaos
1
你的类型问题不在于&的实现不正确,而是truefalse的实现不正确。这些运算符不应该存在于非逻辑类型中。请参考我对你以前问题的回答,了解为什么C#中设计这些运算符。 - CodesInChaos
显示剩余3条评论

2

我会尽力让这个问题简单化,因为我认为有些人把它复杂化了。

var x = 1 & 2;
// behind the scenes: 0001 AND 0010 = 0000
Console.Write(x); // 0, as shown above

在C#中,整数不能用作布尔值。以下代码的结果:

if (1 && 2) // compile error
var x = 1 && 2; // compile error

在C#中,为什么不能使用整数作为布尔值并没有意义,因为类型系统不允许这样做。如果你实现自己的整数类,可以提供从该类型到布尔值的隐式转换,但是int类型不会这样做。在重载时还需要进行选择:您想要按位操作行为还是逻辑行为?两者都无法兼得。

一些语言允许使用0、""和[]作为“falsey”值,但C#不支持。如果你正在进行布尔逻辑运算,请使用bool类型。如果其他方法都失败了,在int上调用Convert.ToBoolean将返回所有非零值的true值。


1
public static MyInt operator &(MyInt l, MyInt r) { return l.val & r.val; }

如果我正确阅读了链接的文章,res = a && b 将会被“展开”为:
MyInt.false(a) ? a : MyInt.&(a, b)

MyInt.false(a) 是 false,因此计算结果为:

MyInt.&(a, b)

扩展为:

a.val & b.val

(1 & 2) == 0,因此false


是的,我从一开始就知道这个,这就是为什么我问了另一个问题。我的问题是为什么它会这样!? - user34537
2
@acidzombie24 为什么不呢?想象一下,你从 Pascal 到 C/C++ ,你是从 #define Begin { #define End } 开始的吗?还是你会阅读语言参考并使用新规则呢? - Nick Martyshchenko
@Mat:事实是bool 1为真,bool 2也为真,但1&&2却不是。 - user34537
@Nick:如果&&是逻辑运算符而不是布尔运算符,那么为什么还要短路它呢?如果它应该像C语言一样(因为很多语法都相似),为什么还要有&&或者让它不像C语言一样是布尔运算符。如果它应该执行逻辑与操作,我就不明白它的用途了。 - user34537
@acidzombie24,我不知道还有什么其他好的解释可以补充。我真的很惊讶你作为一个C++背景的人会问这个问题:(我是从ASM/C/C++背景转到C#的,并始终记住这里的Minimal / Short-circuit evaluation - http://en.wikipedia.org/wiki/Minimal_evaluation。你读过这篇文章吗?你读过https://dev59.com/InI-5IYBdhLWcg3wipBk吗? - Nick Martyshchenko
显示剩余7条评论

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