究竟是什么样鬼澳门皇冠官网app

【转】四、可空类型Nullable<T>到底是怎么着鬼

值类型为啥不可认为空

第一大家都知道引用类型默许值都以null,而值类型的暗中同意值都有非null。

澳门皇冠官网app 1

缘何引用类型可认为空?因为引用类型变量都以保留贰个目的的地址引用(仿佛七个url对应多个页面),而援用类型值为null的时候是变量值指向了四个空援用(仿佛贰个空的url)

澳门皇冠官网app 2

这怎么值不能够有空值呢?其实非常粗略,因为如int值范围是-2147483648到2147483647。个中根本就从未有过给null值留那么三个岗位。

澳门皇冠官网app 3

大家为什么须求用到可空类型

举个栗子吧,大家定义壹个人(Person),它有几个属性出生日期(BeginTime)、病逝日期(EndTime)、年龄(Age)。

一旦此人还健在江湖,请问怎么给已逝世日期赋值?有人很聪明说“为空啊”。是的,那正是大家的要求。

微软在C#2.0的时候就为我们引进了可null值类型( System.Nullable<T> ),那么上边来定义Person类。

 1 public class Person
 2 {
 3     /// <summary>
 4     /// 出生日期
 5     /// </summary>
 6     public DateTime BeginTime { get; set; }
 7     /// <summary>
 8     /// 死亡日期
 9     /// </summary>
10     public System.Nullable<DateTime> EndTiem { get; set; }
11     public int Age
12     {
13         get
14         {
15             if (EndTiem.HasValue)//如果挂了(如果有值,证明死了)
16             {
17                 return (EndTiem.Value - BeginTime).Days;
18             }
19             else//还没挂
20             {
21                 return (DateTime.Now - BeginTime).Days;
22             }
23         }
24     }
25 }

 

如此,大家就足以很轻便得到一人的岁数了。

static void Main(string[] args)
{
    Person p1 = new Person()
    {
        BeginTime = DateTime.Parse("1990-07-19")
    };

    Person p2 = new Person()
    {
        BeginTime = DateTime.Parse("1893-12-26"),
        EndTiem = DateTime.Parse("1976-09-09")
    };

    Console.WriteLine("我今年" + p1.Age + "岁。");
    Console.WriteLine("毛爷爷活了" + p2.Age + "岁。");

    Console.ReadKey();
}

可空类型的完结

大家日前用到了 System.Nullable<DateTime> 来代表可空时间档期的顺序,其实日常大家用得越多的是 DateTime? 直接在类型T前边加二个问号,这两种是千篇一律的。多亏损微软的语法糖。

咱俩来看看 System.Nullable<T> 到底是何物。

澳门皇冠官网app 4

搜噶,原本是贰个布局。还阅览了大家属性的
HasValue和Value属性。原来竟如此轻便。贰个构造两特本性,一个存值,八个存是还是不是有值。那么下边大家也来试试啊。

澳门皇冠官网app 5

不佳意思,让大家失望了。前边大家就说过了,值类型是不可以赋值null的(结构也是值类型)。

如何是好!如何是好!不对啊,微软本身也是概念的组织,它怎么能够一贯赋值null呢。(奇怪,诡异,究竟是人家微松软煦搞得,大概获得了极度的对待吗)

但是,这样就让大家止步了呢?NO!大家都了解,看微软的IL(中间语言)的时候,仿佛脱了它的服装一样,非常多时候不驾驭的地点都足以看个毕竟,上边大家就去脱衣裳。

先是,我们用三种不一致的方法给可空类型赋值。

static void Main(string[] args)
{

    System.Nullable<int> number1 = null;

    System.Nullable<int> number2 = new System.Nullable<int>();

    System.Nullable<int> number3 = 23;

    System.Nullable<int> number4 = new System.Nullable<int>(88);

    Console.ReadKey();
}    

 

然后用reflector看编写翻译后的IL。

澳门皇冠官网app 6

原来是那样,可空类型的赋值直接等效于构造实例。赋null时其实正是调用空构造函数,有值时就就把值传入带参数的构造函数。(否尽泰来屏山乡。如此,大家是否能够跟着下面截图中的 MyNullable<T> 继续模拟可空类型呢?且继续往下看。)

public struct MyNullable<T> where T : struct
{
    //错误    1    结构不能包含显式的无参数构造函数 
    //还好 bool默认值就是false,所以这里不显示为 this._hasValue = false也不会有影响
    //public MyNullable()
    //{
    //    this._hasValue = false;
    //}
    public MyNullable(T value)//有参构造函数
    {
        this._hasValue = true;
        this._value = value;
    }

    private bool _hasValue;

    public bool HasValue//是否不为空
    {
        get { return _hasValue; }
    }

    private T _value;
    public T Value//值
    {
        get
        {
            if (!this._hasValue)//如没有值,还访问就抛出异常
            {
                throw new Exception(" 可为空的对象必须具有一个值");
            }
            return _value;
        }
    }
}

 

哟西,基本三春经模拟出了可空类型出来的。(可是我们依然不能一贯赋值,只好通过构造函数的措施来行使自定义的可空类型)。

全体代码如下:

澳门皇冠官网app 7澳门皇冠官网app 8

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 可空类型
{
    public class Person
    {
        /// <summary>
        /// 出生日期
        /// </summary>
        public DateTime BeginTime { get; set; }
        /// <summary>
        /// 死亡日期
        /// </summary>
        public MyNullable<DateTime> EndTiem { get; set; } //这里改用MyNullable
        /// <summary>
        /// 年龄
        /// </summary>
        public double Age
        {
            get
            {
                if (EndTiem.HasValue)//如果挂了(如果有值,证明死了)
                {
                    return (EndTiem.Value - BeginTime).Days / 365;
                }
                else//还没挂
                {
                    return (DateTime.Now - BeginTime).Days / 365;
                }
            }
        }
    }

    public struct MyNullable<T> where T : struct
    {
        //错误    1    结构不能包含显式的无参数构造函数 
        //还好 bool默认值就是false,所以这里不显示为 this._hasValue = false也不会有影响
        //public MyNullable()
        //{
        //    this._hasValue = false;
        //}
        public MyNullable(T value)//有参构造函数
        {
            this._hasValue = true;
            this._value = value;
        }

        private bool _hasValue;

        public bool HasValue//是否不为空
        {
            get { return _hasValue; }
        }

        private T _value;
        public T Value//值
        {
            get
            {
                if (!this._hasValue)//如没有值,还访问就抛出异常
                {
                    throw new Exception(" 可为空的对象必须具有一个值");
                }
                return _value;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person()
            {
                BeginTime = DateTime.Parse("1990-07-19")
            };

            Person p2 = new Person()
            {
                BeginTime = DateTime.Parse("1893-12-26"),
                EndTiem = new MyNullable<DateTime>(DateTime.Parse("1976-09-09"))//这里使用MyNullable的有参构造函数
            };

            Console.WriteLine("我今年" + p1.Age + "岁。");
            Console.WriteLine("毛爷爷活了" + p2.Age + "岁。");

            Console.ReadKey();
        }

    }
}

View Code

 

和种类的可空类型得出了平等的结果。

澳门皇冠官网app 9

总结

  • 可空类型是组织(也正是值类型)
  • 于是可空类型的null值和引用类型的null是不一样样的。(可空类型的并非引用类型的null,而是用结构的另一种表示方法来代表null)

澳门皇冠官网app 10

 

有同学问,怎么着才得以成功直接赋值呢?那么些自家也从不什么样好的秘技,可能需求编写翻译器的支撑。

上述内容都以瞎说。希望能对你有那么一丝丝用处,谢谢阅读。

(首发链接:http://www.cnblogs.com/zhaopei/p/5537759.html )

 

 


 

============== 2016-06-05更新==============

地点大家建议了疑义“何以才足以造成直接赋值呢”,本来作者是未曾好的化解办法。这里要多谢大家的园友@冲杀给自己提供了好的减轻方案。

implicit(关键字用于注解隐式的客商定义类型调换运算符。)

public static implicit operator MyNullable<T>(T value)
{
       return new MyNullable<T>(value);
}

只须要在 struct
MyNullable<T> 中增多以上代码,就足以一贯赋值了。(成效等效于是直接重写了“=”赋值符号)

澳门皇冠官网app 11

澳门皇冠官网app 12

一体化代码如下:

澳门皇冠官网app 13澳门皇冠官网app 14

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace test
{
    public class Person
    {
        /// <summary>
        /// 出生日期
        /// </summary>
        public DateTime BeginTime { get; set; }
        /// <summary>
        /// 死亡日期
        /// </summary>
        public MyNullable<DateTime> EndTiem { get; set; } //这里改用MyNullable
        /// <summary>
        /// 年龄
        /// </summary>
        public double Age
        {
            get
            {
                if (EndTiem.HasValue)//如果挂了(如果有值,证明死了)
                {
                    return (EndTiem.Value - BeginTime).Days / 365;
                }
                else//还没挂
                {
                    return (DateTime.Now - BeginTime).Days / 365;
                }
            }
        }
    }

    public struct MyNullable<T> where T : struct
    {
        //错误    1    结构不能包含显式的无参数构造函数 
        //还好 bool默认值就是false,所以这里不显示为 this._hasValue = false也不会有影响
        //public MyNullable()
        //{
        //    this._hasValue = false;
        //} 

        public MyNullable(T value)//有参构造函数
        {
            this._hasValue = true;
            this._value = value;
        }

        private bool _hasValue;

        public bool HasValue//是否不为空
        {
            get { return _hasValue; }
        }

        private T _value;
        public T Value//值
        {
            get
            {
                if (!this._hasValue)//如没有值,还访问就抛出异常
                {
                    throw new InvalidOperationException(" 可为空的对象必须具有一个值");
                }
                return _value;
            }
        }

        public static implicit operator MyNullable<T>(T value)
        {
            return new MyNullable<T>(value);
        } 
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person()
            {
                BeginTime = DateTime.Parse("1990-07-19")
            };

            Person p2 = new Person()
            {
                BeginTime = DateTime.Parse("1893-12-26"),
                EndTiem = DateTime.Parse("1976-09-09") 
                //new MyNullable<DateTime>(DateTime.Parse("1976-09-09"))
                //这里使用MyNullable的有参构造函数
            };

            Console.WriteLine("我今年" + p1.Age + "岁。");
            Console.WriteLine("毛爷爷活了" + p2.Age + "岁。"); 

            Console.ReadKey();
        }

    }
}

View Code

 

如此这般,我们早就产生了自定义可空类型的直白赋值。但只是一些,假诺想要赋值null呢?

澳门皇冠官网app 15

大同小异照旧出现了最早步的编写翻译错误。我们想到既然上边的值赋值能够重新(隐式调换),那null应该也可以啊(null是援用类型的贰个特定值)。

再加贰个重载:

//隐式转换
public static implicit operator MyNullable<T>(string value)
{
    if (value == null)
        return new MyNullable<T>();
    throw new Exception("赋值右边不能为字符串");
    //这里不知道是否可以在编译期间抛出错误(或者怎样限制只能传null)
}

 

那样能够知足大家的须要了(并无极度)。

澳门皇冠官网app 16

惋惜美中不足,如若给 p2.EndTiem 赋值叁个非空字符串时,要运维时才会报错(而系统的可空类型会在编写翻译期就报错)。不领会大神们可有解!!

即便那样,能一气呵成直接赋值依旧让自家小小激动了一把。为此,特意查了下首要字 implicit operator ,又是让本人不大激动了一把,大家不但能够“重写”赋值,大家仍可以“重写”+

  • * / % & | ^ << >> == != > < >= <=等运算符。

上边我们先来“重写”下自定义可空类型的相比(==)运算符。

//"重写"比较运算符
public static bool operator ==(MyNullable<T> operand, MyNullable<T> operand2)
{
    if (!operand.HasValue && !operand2.HasValue)
    {
        return true;
    }
    else if (operand.HasValue && operand2.HasValue)
    {
        if (operand2.Value.Equals(operand.Value))
        {
            return true;
        }
    }
    return false;
}

//"重写"比较运算符
public static bool operator !=(MyNullable<T> operand, MyNullable<T> operand2)
{
    return !(operand == operand2);
}

 

Console.WriteLine("p1.EndTiem == null," + (p1.EndTiem == null).ToString());
Console.WriteLine("p2.EndTiem == null," + (p2.EndTiem == null).ToString());
Console.WriteLine("p1.EndTiem == DateTime.Parse(1976-09-09)," + (p1.EndTiem == DateTime.Parse("1976-09-09")).ToString());
Console.WriteLine("p2.EndTiem == DateTime.Parse(1976-09-09)," + (p2.EndTiem == DateTime.Parse("1976-09-09")).ToString());

p1.EndTiem = DateTime.Parse("2016-06-06");
p2.EndTiem = null;
Console.WriteLine();
Console.WriteLine("赋值 p1.EndTiem = DateTime.Parse(2016-06-06)  p2.EndTiem = null 后:");
Console.WriteLine("p1.EndTiem == null," + (p1.EndTiem == null).ToString());
Console.WriteLine("p2.EndTiem == null," + (p2.EndTiem == null).ToString());
Console.WriteLine("p1.EndTiem == DateTime.Parse(2016-06-06)," + (p1.EndTiem == DateTime.Parse("2016-06-06")).ToString());
Console.WriteLine("p2.EndTiem == DateTime.Parse(2016-06-06)," + (p2.EndTiem == DateTime.Parse("2016-06-06")).ToString());

澳门皇冠官网app 17

结果完全符合!

一体化代码如下:

澳门皇冠官网app 18澳门皇冠官网app 19

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace test
{
    public class Person
    {
        /// <summary>
        /// 出生日期
        /// </summary>
        public DateTime BeginTime { get; set; }
        /// <summary>
        /// 死亡日期
        /// </summary>
        public MyNullable<DateTime> EndTiem { get; set; } //这里改用MyNullable
        /// <summary>
        /// 年龄
        /// </summary>
        public double Age
        {
            get
            {
                if (EndTiem.HasValue)//如果挂了(如果有值,证明死了)
                {
                    return (EndTiem.Value - BeginTime).Days / 365;
                }
                else//还没挂
                {
                    return (DateTime.Now - BeginTime).Days / 365;
                }
            }
        }
    }

    public struct MyNullable<T> where T : struct
    {
        //错误    1    结构不能包含显式的无参数构造函数 
        //还好 bool默认值就是false,所以这里不显示为 this._hasValue = false也不会有影响
        //public MyNullable()
        //{
        //    this._hasValue = false;
        //} 

        public MyNullable(T value)//有参构造函数
        {
            this._hasValue = true;
            this._value = value;
        }

        private bool _hasValue;

        public bool HasValue//是否不为空
        {
            get { return _hasValue; }
        }

        private T _value;
        public T Value//值
        {
            get
            {
                if (!this._hasValue)//如没有值,还访问就抛出异常
                {
                    throw new InvalidOperationException(" 可为空的对象必须具有一个值");
                }
                return _value;
            }
        }

        //隐式转换
        public static implicit operator MyNullable<T>(T value)
        {
            return new MyNullable<T>(value);
        }

        //隐式转换
        public static implicit operator MyNullable<T>(string value)
        {
            if (value == null)
                return new MyNullable<T>();
            throw new Exception("赋值右边不能为字符串");
            //这里不知道是否可以在编译期间抛出错误(或者怎样限制只能传null)
        }

        //"重写"比较运算符
        public static bool operator ==(MyNullable<T> operand, MyNullable<T> operand2)
        {
            if (!operand.HasValue && !operand2.HasValue)
            {
                return true;
            }
            else if (operand.HasValue && operand2.HasValue)
            {
                if (operand2.Value.Equals(operand.Value))
                {
                    return true;
                }
            }
            return false;
        }

        //"重写"比较运算符
        public static bool operator !=(MyNullable<T> operand, MyNullable<T> operand2)
        {
            return !(operand == operand2);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person()
            {
                BeginTime = DateTime.Parse("1990-07-19")
            };

            Person p2 = new Person()
            {
                BeginTime = DateTime.Parse("1893-12-26"),
                EndTiem = DateTime.Parse("1976-09-09")
                //new MyNullable<DateTime>(DateTime.Parse("1976-09-09"))
                //这里使用MyNullable的有参构造函数
            };

            Console.WriteLine("我今年" + p1.Age + "岁。");
            Console.WriteLine("毛爷爷活了" + p2.Age + "岁。");
            Console.WriteLine();

            Console.WriteLine("p1.EndTiem == null," + (p1.EndTiem == null).ToString());
            Console.WriteLine("p2.EndTiem == null," + (p2.EndTiem == null).ToString());
            Console.WriteLine("p1.EndTiem == DateTime.Parse(1976-09-09)," + (p1.EndTiem == DateTime.Parse("1976-09-09")).ToString());
            Console.WriteLine("p2.EndTiem == DateTime.Parse(1976-09-09)," + (p2.EndTiem == DateTime.Parse("1976-09-09")).ToString());

            p1.EndTiem = DateTime.Parse("2016-06-06");
            p2.EndTiem = null;
            Console.WriteLine();
            Console.WriteLine("赋值 p1.EndTiem = DateTime.Parse(2016-06-06)  p2.EndTiem = null 后:");
            Console.WriteLine("p1.EndTiem == null," + (p1.EndTiem == null).ToString());
            Console.WriteLine("p2.EndTiem == null," + (p2.EndTiem == null).ToString());
            Console.WriteLine("p1.EndTiem == DateTime.Parse(2016-06-06)," + (p1.EndTiem == DateTime.Parse("2016-06-06")).ToString());
            Console.WriteLine("p2.EndTiem == DateTime.Parse(2016-06-06)," + (p2.EndTiem == DateTime.Parse("2016-06-06")).ToString());     

            Console.ReadKey();
        }

    }
}

View Code

 

 

调换关键字:operator、explicit与implicit分析资料:http://www.cnblogs.com/hunts/archive/2007/01/17/operator_explicit_implicit.html

我们还足以玩出越来越多的花样!!!

 

正文已协同至《C#基础知识巩固类别

相关文章