none
写代码时发现的小问题,有点纳闷. RRS feed

  • 问题


  •             private System.Windows.Media.Color _Color;
                public byte R
                {
                    get
                    {
                        if (this._Color == null)
                        {
                            this._Color = new Color();
                        }
                        return this._Color.R;
                    }
                    set
                    {
                        if (this._Color == null)
                        {
                            this._Color = new Color();
                        }
                        this._Color.R = value;
                    }
                }
    这是我写代码时敲上的.字段和属性...
    后来一看,发现Color是个struct,和null进行判断没有报错...
    这是为什么呢?
    我看好像也没用到可空值类型.
    用的空值设计模式吗?这个模式我还没太仔细看..看书时见到过..
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年11月11日 2:57

答案

  • 你好!

    的确是这样的,没有报错的原因是 System.Windows.Media.Color 这个结构中重载了内置运算符 == 和 !=

    public static bool operator !=(Color color1, Color color2) { ... }
    public static bool operator ==(Color color1, Color color2) { ... }


    知识改变命运,奋斗成就人生!
    2009年11月11日 9:49
    版主
  • 终于找到这个帖子了啊!

    通过几天的研究,问题终于有了结果了,分享给大家!


    我也是请教了一个朋友才了解到这些的,十分感谢懿民帮助 !

    通过查看C#编译器的源代码,我们会发现他在编译以下代码的时候:

                public static bool operator ==(MyStruct2 s1, MyStruct2 s2)
                {
                    return s1.Value == s2.Value;
                }

    需要执行如下代码:

            // Equality operators are special. If the return type is bool and the parameter types
            // are the same then they treat null as a value and return bool.
            if (fEqOp && nin.FAlwaysNull()) {
                ASSERT((ek == EK_EQ || ek == EK_NE) && typeRetRaw->isPredefType(PT_BOOL) && paramsRaw->Item(0) == paramsRaw->Item(1));
                // At least one of them is a constant null.
                EXPR * exprRes;
     
                if (nin.rgfNull[0] && nin.rgfNull[1]) {
                    // Both are constant nulls.
                    exprRes = newExprConstant(tree, typeRetRaw, ConstValInit(ek == EK_EQ));
                    exprRes = AddSideEffects(tree, exprRes, exprVal2, true, true);
                    return AddSideEffects(tree, exprRes, exprVal1, true, true);
                }
     
                if (nin.rgfNull[0] ? !exprVal2->type->isNUBSYM() : !exprVal1->type->isNUBSYM()) {
                    // One is null and the other is not nullable.
                    exprRes = newExprConstant(tree, typeRetRaw, ConstValInit(ek == EK_NE));
                    exprRes = AddSideEffects(tree, exprRes, exprVal2, true, true);
                    return AddSideEffects(tree, exprRes, exprVal1, true, true);
                }
     
                // Generate seq(a, !b.HasValue) or rev(!a.HasValue, b).
                exprRes = BindNubHasValue(tree, nin.rgfNull[0] ? exprVal2 : exprVal1, ek == EK_NE);
                return AddSideEffects(tree, exprRes, nin.rgfNull[0] ? exprVal1 : exprVal2, nin.rgfNull[0], true);
            }

    大家注意最上面那行注释:那么判等操作符是特殊的,如果返回值是bool并且参数类型是一样的,那么就把null作为值类型来看待,返回一个bool.

    可 见,C#编译器是有意这样来设计的,这不是一个bug,值类型只要提供了==运算符重载,并且参数类型是一样的,值类型就可以和null 比较(==,!=,<,>,<=,>=都有类似的特殊性),但是结果始终是确定的,所以这个比较意义不是很大,但是C#编译器的 确是允许类似的比较的,他认为这个行为是合法的!这样设计的原因很可能是为了和可空类型在语义上保持一致!
    周雪峰
    2009年11月17日 10:59
    版主

全部回复

  • 试试和Color.Empty比较看

    2009年11月11日 3:01
  • 你好!
         结构体是无法和null做比较的,会出现编译错误!
        
    周雪峰
    2009年11月11日 5:04
    版主
  • 雪峰版主试下嘛...
    我这样写编译器没有报错的...
    就因为感觉纳闷才发上来的...
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年11月11日 7:02
  • 我目前的环境没有安装这个dll,所以没有办法实际测试啊!
    周雪峰
    2009年11月11日 8:04
    版主
  • 结构初始化已经实例了 有值的  那个条件通不过new实例化 所以不报错的
    可能作者失误把它当成类写了那个判断

    2009年11月11日 8:55
  • 哎呀,同志们试一下拉..
    上面那段代码是我不经意顺手写出来的...一直能够正常使用.
    后来发现Color是值类型的struct..
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年11月11日 9:09
  • 我已经测试了 我还没搞清楚你这个问题是什么? 没说明
    2009年11月11日 9:18
  • 你好!

    的确是这样的,没有报错的原因是 System.Windows.Media.Color 这个结构中重载了内置运算符 == 和 !=

    public static bool operator !=(Color color1, Color color2) { ... }
    public static bool operator ==(Color color1, Color color2) { ... }


    知识改变命运,奋斗成就人生!
    2009年11月11日 9:49
    版主
  • 重载可以达到跨越值类型和引用类型?
    哦了~呵呵....
    就是写到这觉得有点纳闷...
    操作符重载还有这功能.....
    3Q了X.X.Y版主~
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年11月11日 10:18
  • 你好!

    的确是这样的,没有报错的原因是 System.Windows.Media.Color 这个结构中重载了内置运算符 == 和 !=

    public
     static
     bool
     operator
     !=(Color color1, Color color2) { ... }
    public
     static
     bool
     operator
     ==(Color color1, Color color2) { ... }
    


    知识改变命运,奋斗成就人生!
    请教一下XXY,这里和重载的参数类型不符合啊!

    周雪峰
    2009年11月11日 17:30
    版主
  • 不太明白你的意思,参数类型即结构本身。我写了个测试,如下:

    using System;
    
    namespace StructOperatorDemo
    {
        class Program
        {
            public struct MyStruct1
            {
                public Guid UniqueId;
                public MyStruct1(Guid fUniqueId)
                {
                    this.UniqueId = fUniqueId;
                }
            }
    
            public struct MyStruct2
            {
                public int Value;
                public MyStruct2(int fValue)
                {
                    this.Value = fValue;
                }
    
                public static bool operator !=(MyStruct2 s1, MyStruct2 s2) 
                {
                    return s1.Value != s2.Value;
                }
                public static bool operator ==(MyStruct2 s1, MyStruct2 s2) 
                {
                    return s1.Value == s2.Value;
                }
    
                public override int GetHashCode()
                {
                   return this.Value;
                }
            }
    
            static void Main(string[] args)
            {
                // 错误: 运算符“==”无法应用于 “MyStruct1” 和 “<null>” 类型
                // MyStruct1 myStruct1 = new MyStruct1();
                // if (myStruct1 == null)
                // {
     
                // }
    
                // 正确
                MyStruct2 myStruct2 = new MyStruct2();
                if (myStruct2 == null)
                {
     
                }
            }
        }
    }
    


    知识改变命运,奋斗成就人生!
    2009年11月12日 13:00
    版主
  • 这个问题相当的有意思啊!我用上面XXY版主的例子,详细说说我的看法:
    using System;
    
    namespace StructOperatorDemo
    {
        class Program
        {
            public struct MyStruct1
            {
                public Guid UniqueId;
                public MyStruct1(Guid fUniqueId)
                {
                    this.UniqueId = fUniqueId;
                }
            }
    
            public struct MyStruct2
            {
                public int Value;
                public MyStruct2(int fValue)
                {
                    this.Value = fValue;
                }
    
                public static bool operator !=(MyStruct2 s1, MyStruct2 s2) 
                {
                    return s1.Value != s2.Value;
                }
                public static bool operator ==(MyStruct2 s1, MyStruct2 s2) 
                {
                    return s1.Value == s2.Value;
                }
    
                public override int GetHashCode()
                {
                   return this.Value;
                }
            }
    
            static void Main(string[] args)
            {
                // 错误: 运算符“==”无法应用于 “MyStruct1” 和 “<null>” 类型
                // MyStruct1 myStruct1 = new MyStruct1();
                // if (myStruct1 == null)
                // {
     
                // }
    
                // 正确
                MyStruct2 myStruct2;
                if (myStruct2 == null)
                {
                       Console.WriteLine("myStruct2==null"); 
                }
            }
        }
    }
    
    这里重载了==运算符就使得myStuct2==null通过了编译,情况似乎有些诡异,因为貌似==两边的类型无法匹配重载的定义!
    首先,我们尝试在==运算符重载方法中加断点,结果观察到里面的代码并没有执行。
    自从.NET Framework2.0引入了可空类型,只要你定义了==运算符重载(两个参数类型必须都是原类型才可以),这里C#编译器会认可这个判等表达式,由于一个值类型的实例永远不可能等于可空类型的null,所以这里C#编译器智能的认为这个判等表达式永远为false,我们来看看Main方法的IL
    .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint
      // 代码大小       9 (0x9)
      .maxstack  2
      .locals init ([0] valuetype ConsoleApplication1.MyStruct2 myStruct2,
               [1] bool CS$4$0000)
      IL_0000:  nop
      IL_0001:  ldc.i4.0
      IL_0002:  ldc.i4.0
      IL_0003:  ceq         //这里比较的是0和0,这个行为很有趣,==结果是固定的情况下就比较0和0
      IL_0005:  stloc.1    //比较结果放入临时变量CS$4$0000中,但是以后根本就不使用了!
      IL_0006:  br.s       IL_0008    //无条件跳转,if里的代码并没有生成IL
      IL_0008:  ret
    } // end of method Program::Main
    这里值类型和null的判等比较只有当重载==运算符,并且两个参数类型必须都是原类型才可以,比如我们这样修改重载方法:
                public static bool operator ==(MyStruct2 s1, MyStruct1 s2) //修改第二个参数类型
                {
                    return s1.Value == s2.GetHashCode();
                }
    
    这样会出现编译错误了:运算符“==”无法应用于 “MyStruct1” 和 “<null>” 类型
    实际上由于值类型和null的比较结果是一定的,所以也没有必要做判等比较,这里只是观察一下C#编译器的一些行为,希望把一些有趣的现象分享给大家!
    周雪峰
    2009年11月13日 3:12
    版主
  • 终于找到这个帖子了啊!
    研究了好几天了,终于有了结果,现在把结果分享给大家!
    我也是请教了一个朋友才了解到这些的,十分感谢懿民帮助 !

    通过查看C#编译器的源代码,我们会发现他在编译以下代码的时候:

                public static bool operator ==(MyStruct2 s1, MyStruct2 s2)
                {
                    return s1.Value == s2.Value;
                }

    需要执行如下代码:

            // Equality operators are special. If the return type is bool and the parameter types
            // are the same then they treat null as a value and return bool.
            if (fEqOp && nin.FAlwaysNull()) {
                ASSERT((ek == EK_EQ || ek == EK_NE) && typeRetRaw->isPredefType(PT_BOOL) && paramsRaw->Item(0) == paramsRaw->Item(1));
                // At least one of them is a constant null.
                EXPR * exprRes;
     
                if (nin.rgfNull[0] && nin.rgfNull[1]) {
                    // Both are constant nulls.
                    exprRes = newExprConstant(tree, typeRetRaw, ConstValInit(ek == EK_EQ));
                    exprRes = AddSideEffects(tree, exprRes, exprVal2, true, true);
                    return AddSideEffects(tree, exprRes, exprVal1, true, true);
                }
     
                if (nin.rgfNull[0] ? !exprVal2->type->isNUBSYM() : !exprVal1->type->isNUBSYM()) {
                    // One is null and the other is not nullable.
                    exprRes = newExprConstant(tree, typeRetRaw, ConstValInit(ek == EK_NE));
                    exprRes = AddSideEffects(tree, exprRes, exprVal2, true, true);
                    return AddSideEffects(tree, exprRes, exprVal1, true, true);
                }
     
                // Generate seq(a, !b.HasValue) or rev(!a.HasValue, b).
                exprRes = BindNubHasValue(tree, nin.rgfNull[0] ? exprVal2 : exprVal1, ek == EK_NE);
                return AddSideEffects(tree, exprRes, nin.rgfNull[0] ? exprVal1 : exprVal2, nin.rgfNull[0], true);
            }

    大家注意最上面那行注释:那么判等操作符是特殊的,如果返回值是bool并且参数类型是一样的,那么就把null作为值类型来看待,返回一个bool.

    可 见,C#编译器是有意这样来设计的,这不是一个bug,值类型只要提供了==运算符重载,并且参数类型是一样的,值类型就可以和null 比较(==,!=,<,>,<=,>=都有类似的特殊性),但是结果始终是确定的,所以这个比较意义不是很大,但是C#编译器的 确是允许类似的比较的,他认为这个行为是合法的!这样设计的原因很可能是为了和可空类型在语义上保持一致!
    周雪峰
    2009年11月17日 10:53
    版主
  • 终于找到这个帖子了啊!

    通过几天的研究,问题终于有了结果了,分享给大家!


    我也是请教了一个朋友才了解到这些的,十分感谢懿民帮助 !

    通过查看C#编译器的源代码,我们会发现他在编译以下代码的时候:

                public static bool operator ==(MyStruct2 s1, MyStruct2 s2)
                {
                    return s1.Value == s2.Value;
                }

    需要执行如下代码:

            // Equality operators are special. If the return type is bool and the parameter types
            // are the same then they treat null as a value and return bool.
            if (fEqOp && nin.FAlwaysNull()) {
                ASSERT((ek == EK_EQ || ek == EK_NE) && typeRetRaw->isPredefType(PT_BOOL) && paramsRaw->Item(0) == paramsRaw->Item(1));
                // At least one of them is a constant null.
                EXPR * exprRes;
     
                if (nin.rgfNull[0] && nin.rgfNull[1]) {
                    // Both are constant nulls.
                    exprRes = newExprConstant(tree, typeRetRaw, ConstValInit(ek == EK_EQ));
                    exprRes = AddSideEffects(tree, exprRes, exprVal2, true, true);
                    return AddSideEffects(tree, exprRes, exprVal1, true, true);
                }
     
                if (nin.rgfNull[0] ? !exprVal2->type->isNUBSYM() : !exprVal1->type->isNUBSYM()) {
                    // One is null and the other is not nullable.
                    exprRes = newExprConstant(tree, typeRetRaw, ConstValInit(ek == EK_NE));
                    exprRes = AddSideEffects(tree, exprRes, exprVal2, true, true);
                    return AddSideEffects(tree, exprRes, exprVal1, true, true);
                }
     
                // Generate seq(a, !b.HasValue) or rev(!a.HasValue, b).
                exprRes = BindNubHasValue(tree, nin.rgfNull[0] ? exprVal2 : exprVal1, ek == EK_NE);
                return AddSideEffects(tree, exprRes, nin.rgfNull[0] ? exprVal1 : exprVal2, nin.rgfNull[0], true);
            }

    大家注意最上面那行注释:那么判等操作符是特殊的,如果返回值是bool并且参数类型是一样的,那么就把null作为值类型来看待,返回一个bool.

    可 见,C#编译器是有意这样来设计的,这不是一个bug,值类型只要提供了==运算符重载,并且参数类型是一样的,值类型就可以和null 比较(==,!=,<,>,<=,>=都有类似的特殊性),但是结果始终是确定的,所以这个比较意义不是很大,但是C#编译器的 确是允许类似的比较的,他认为这个行为是合法的!这样设计的原因很可能是为了和可空类型在语义上保持一致!
    周雪峰
    2009年11月17日 10:59
    版主
  • 晕.我没有取消通知,为什么有回答时没有给我邮件呢...
    雪峰版主的代码是哪里的?SSCLI吗?
    非常感谢....
    其实有些细节真正注意到的时候才会发现..
    发现了问题,发上来大家一起研究一起学习~呵呵...
    有人说,充满技术的生活枯燥无味.. 我笑他们不懂.因为只有技术才能充实生活.. 学习就像生活,都需要善于总结,才能发现问题,取得进步.. 生活中充满了数学算式与结构,只要我们善于观察和思考..
    2009年11月20日 6:28
  • 很高兴和大家讨论这个问题啊!
    周雪峰
    2009年11月20日 6:42
    版主