C# 数据类型区别和类型转换

数据类型的区别

栈内存和堆内存

概念

堆(Heap):在c里面叫堆,在c#里面其实叫托管堆。

栈(Stack):就是堆栈,简称栈。

托管堆:

托管堆不同于堆,它是由CLR(公共语言运行库(Common Language Runtime))管理,当堆中满了之后,会自动清理堆中的垃圾。所以,做为.net开发,我们不需要关心内存释放的问题。

栈区的特性:先进后出 入栈的时候是123按顺序入的,那么出栈的时候就是321按顺序出。

当一个栈结束时会把存在栈中的数据依次出栈。

内存堆栈:存在内存中的两个存储区(堆区,栈区)。

栈区:存放函数的参数、局部变量、返回数据等值,由编译器自动释放

堆区:存放着引用类型的对象,由CLR释放

堆是一块内存区域,在堆里可以分配大块的内存用于存储某类型的数据对象。与栈不同,堆里的内存能够以任意顺序存入和移除,除了string 先进先出。虽然程序可以在堆里保存数据,但并不能显式地删除它们。CLR的自动GC(Garbage Collector,垃圾收集器)在判断出程序的代码将不会再访问某数据项时,自动清除无主的堆对象。

课外阅读

课外阅读

值类型和引用类型的区别

图1
图2

  1. 值类型存取速度快,引用类型存取速度慢。
  2. 值类型表示实际数据,引用类型表示存储在堆中的数据的引用和地址。
  3. 值类型都继承自System.VlueTyp,引用类型都继承自System.Object。
  4. 栈中的内存是自由分配自动释放的,而引用类型会由.NET的GC来回收释放。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _01_数据类型的区别
{
    internal class Program
    {
     
        static void Main(string[] args)
        {



            //值类型的变量存储的是值本身(存储到栈中)
            //引用类型的变量存储的是数据的内存地址(存储到堆中,变量保存的是内存地址)


            //= 运算符在进行赋值操作时,操作的是栈中保存的内容
            //= 运算符在进行复值操作时,操作的是栈中保存的内容


            int a = 12;
            int b = a;
            a = 13;      //图1,图2详解
            Console.WriteLine(a);//13
            Console.WriteLine(b);//12
            //因为int类型属于值类型,变量存储的是值本身,所以当一个值类型的实例被赋值给另一个变量时,数据的一个完整副本会被创建并分配给新变量。原始变量与新变量彼此独立,互不影响。


            Book book1 = new Book();
            book1.Name = "隐杀";
            Book book2 = book1;     //图1,图2详解
            book1.Name = "道诡异仙";  
            Console.WriteLine(book1.Name);//道诡异仙
            Console.WriteLine(book2.Name);//道诡异仙
            //因为Book类属于引用型,当一个引用类型赋值给另一个变量时,赋值的是引用(内存地址)。这意味着两个变量指向堆上的同一个对象,因此一个变量的改变会影响到另一个。


            People p1 = new People() { Name="白小村"};//声明时同时初始化

            People p2 = p1;
            p2 = new People() { Name = "涂红红" };//虽然在上步骤p1的内存地址复制给了p2,但在这里 new People() { Name = "涂红红" }等于p1新开辟了一块内存空间,不再与p2共用同一块内存空间
            Console.WriteLine(p1.Name);//白小村
            Console.WriteLine(p2.Name);//涂红红



            Peoples laowang=new Peoples() { Name="小王"};
            Peoples xiaowang=new Peoples() { Name="老王"};

            xiaowang.Father = laowang;
            Console.WriteLine($"名字叫做{xiaowang.Name},父亲叫做{xiaowang.Father}");



            //string 引用数据类型
            //不是一个特别严谨的注释:虽然字符串是引用类型,但是C#中对他进行了特殊处理,可以把他当做基本数据类型使用(后续详解)


            string str1 = "白良苗";
            string str2 = str1;
            str1 = "白灵淼";
            Console.WriteLine(str1);//白灵淼
            Console.WriteLine(str2);//白良苗


        }
    }
    class Book
    {
        public double Price { get; set; }
        public string Name { get; set; }
    }
    class People
    {
        public string Name { get; set; }
    }
    class Peoples
    {
        public string Name { get; set; }
        //对象的成员也可能是一个对象
        public Peoples Father { get; set; }
    }
    
}

特别注意:

在 C# 中,string 类型被视为引用类型。但是,与其他引用类型不同的是,对 string 变量的操作被视为对值类型的操作,因此,在对 string 变量进行赋值或传递到方法时,其实是进行值传递。

这意味着,当您将一个字符串变量传递给一个方法时,该方法会接收该字符串的一个副本,而不是原始字符串的引用。任何对传递的字符串副本的修改都不会影响原始字符串。

值类型和引用类型的区别在于,值类型存储在堆栈中,而引用类型存储在堆中。当您创建一个字符串变量时,它实际上是一个引用类型,但是 .NET 框架会将其优化为值类型,以提高性能。

需要注意的是,字符串是不可变的,即一旦创建就无法更改。因此,在对字符串进行操作时,实际上是创建了一个新的字符串对象,而不是修改原始字符串对象。这也是为什么 C# 中的字符串变量是值传递的原因。

总之,在 C# 中,string 类型是引用类型,但是在对其进行操作时,会被视为值类型,因此是值传递的。

值类型补充-结构体

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

namespace _02_值类型补充_结构体
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //结构体可以让变量存储多个数据(值类型)
            //用法和形式与类基本相同,可以理解为: 结构体就是值类型的对象
            //结构体的使用:1.结构体的声明2.结构体的创建


            //2.结构体的创建
            Book book1 = new Book() { Name="隐杀",Price=39.9,Description="杀手穿越"};
            Book book2 = new Book() { Name = "道诡异仙", Price = 49.9, Description = "精神病更精神" };
            Console.WriteLine($"book1的名字{book1.Name},售价{book1.Price},讲述了{book1.Description}的故事");//隐杀
            Console.WriteLine($"book2的名字{book2.Name},售价{book2.Price},讲述了{book2.Description}的故事");//道诡异仙

            Book book3 = book1;
            book1.Name = "我的26岁女房客";
            book1.Description = "白狗过隙,天空之城";
            Console.WriteLine($"book1的名字{book1.Name},售价{book1.Price},讲述了{book1.Description}的故事");//我的26岁女房客
            Console.WriteLine($"book3的名字{book3.Name},售价{book3.Price},讲述了{book3.Description}的故事");
            //还是 隐杀 因为结构体是值类型,所以改变book1的成员变量不会影响到book3


        }
    }
    //1. 结构体的声明(类似于类的声明)
    struct Book 
    { 
        public string Name;
        public double Price;
        public string Description;
        public int Count;
    
    
    
    }

}

可空数据类型

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

namespace _03_可空类型数据
{
    internal class Program
    {
        static void Main(string[] args)
        {


            //值类型能存储的数据只有这个类型的所有数据,例如byte只能存储0到255之间的整数,bool只能存储true和false

            byte bt = 255;
            int ints = 10086;
            bool b1 = true;
            //值类型都有默认值
            //整数类型的默认值都是0
            //char类型的默认值是\0
            //bool类型的默认值是False
            //结构体默认为空的结构体

            //类中成员的默认值是对应数据类型的默认值, 如public string Name { get; set; },的默认值是字符串的默认值即为""
            Console.WriteLine(new People().Name);//""
            Console.WriteLine(new People().Id);//0




            //引用类型除了可以存储对应的类型外 还可以存一个null值

            string s = "123";
            s = null;
            People p1=new People();
            p1 = null;



            //可空数据类型就是让值类型可以存储null
            //在数据类型的后面添加一个 ? 表示它是一个可空数据类型,将会在本来存储的范围上加上一个null值,同时它的默认值就为null

            int?i2 = null;
            char?c2 = 'a';

            //可空数据类型到不空数据类型的转换
            //如果一个值为可空类型,我们在运算时,会出现问题,所有需要将可空数据类转化为不可空数据类型
            //int num = 10 + i2;

            int?i3=null;
            //int i4=i3;  //报错因为i3可能为null i4不能为null

            
            //可以使用??运算符进行处理
            //执行逻辑:如果??前面的值为null 则使用后面的值
            int i4=i3 ?? 0;
            Console.WriteLine(i4);//0


        }
    }
    class People
    {
        public string Name { get; set; }
        public int Id { get; set; }
    }
}

控制台输入的接收

           //获取到用户输入的字符,只能获取一个字符
            ConsoleKeyInfo  s  = Console.ReadKey();
            //通过ConsoleKeyInfo的Key,获取当前按下的键位
            Console.WriteLine($"按下的键是{s.Key}");
            //通过ConsoleKeyInfo的KeyChar,获取当前按下的键值
            Console.WriteLine($"按下的键是{s.KeyChar}");
           
            
            //Console.ReadLine()获取用户输入的一行数据,按回车键结束输入
            string line = Console.ReadLine();
            Console.WriteLine(line);

类型转换

类型转换就是将A类型的变量赋值给B类型的变量

隐式类型转换

隐式转换(自动转换),就是从小范围到大范围的转换

  		    int i1 = 22;
            long l1 = i1;

            //short s1 = i1; 不允许 因为int存的多

            // ulong ul1 = i1; 不允许  因为int能存负数 ulong不能存负数
            // ushort us1 = i1;  不允许 因为int存的多 ushort存的少 并且不能存负数


            sbyte sbyte2 = 5;
            //byte byte2 = sbyte2; 不允许 因为sbyte可以存负数 byte不能存负数

            byte by3 = 6;
            //sbyte sby3 = by3; 不允许 因为byte最大能存到255 sbyte 只能存到127


            ushort us4 = 21; //无符号
            int int4 = us4; //可以转成有符号的int
            uint uint4 = us4;//可以转为无符号的int

            short sh5 = 33; //有符号
            int int5 = sh5; //可以转为有符号的int
                            //uint uint5 = sh5;  不能转换为无符号的uint
                            // ulong ul5 = sh5; 不能转换为无符号的ulong

            /*
             * 总结:
             * 1.小范围能转大范围,大范围不能转小范围
             * 2.同级别之间都不能转换**加粗样式**
             * 3.无符号可以转换大范围的有符号和无符号
             * 4.有符号可以转换大范围有符号,不能转无符号
             */

            //小数的转换

            //float < double

            float f1 = 2.2f;
            double d1 = f1;

            //整数可以直接转小数

            ulong i6 = 3;
            float f2 = i6;
            double d2 = i6;
            double d4 = 3.3;

            //int i7 = d4;// 不允许 因为整型不能保留小数位

            //派生类:子类   基类:父类  所有类型的基类都是object

            //从派生类转为基类也可以转换
            int i8 = 100;
            char s= 'a';
            object o1=s;
显示类型转换

从大范围到小范围的转换

     //从大范围到小范围的转换
            uint int1 = 20;
            //在被转换的数据前面加上(目标类型)进行显式转换
            int int2 = (int)int1;

            //强制转换可能会导致数据丢失
            uint int3 = 256;
            byte byte1 = (byte)int3;
            Console.WriteLine(byte1);//0


            double dou1 = 22.9;
            int in4 =(int)dou1;
            Console.WriteLine(in4);


            //并不是所有的显式转换都可以转换
            string s1 = "吴亦凡";
            //char c1 =(char)s1;
            char c2 = 'A';
            // string s2=(string)c2;



            //char类型和int类型之间转换根据的是ASCII码转换的
            //https://blog.csdn.net/weixin_45788237/article/details/136460300?spm=1001.2014.3001.5501
            int in5 = (int)c2;
            Console.WriteLine(in5);//65
            int in6 = 67;
            char c3 = (char)in6;
            Console.WriteLine(c3);//C
字符串和其他类型之间的转换
          // ----------其他类型转字符串
           int in1 = 123;
            double do1 = 12.3;
            bool b = true;
            string st1 = in1.ToString();
            string st2 = do1.ToString();
            string st3 = b.ToString();
            Console.WriteLine(st1);  // 123==> "123"
            Console.WriteLine(st2);  // 12.3 ==> "12.3"
            Console.WriteLine(st3);  // true ==> "true"

            int aa = 1008611;
            Console.WriteLine(aa.ToString());// "1008611"

            Console.WriteLine(aa.ToString("F1"));// "1008611.0"
            Console.WriteLine(aa.ToString("F2"));// "1008611.00"
            //三位分割
            Console.WriteLine(aa.ToString("n"));// 1,008,611.00
            //加上¥前缀(还会有n的特性)
            Console.WriteLine(aa.ToString("c"));// ¥1,008,611.00
            //----------字符串转其他类型
            int in7 =int.Parse(st1);
            double dou2 =double.Parse(st2);
            bool b3 = bool.Parse(st3);


            int in8 =int.Parse("123");
           // in8 = int.Parse("123.0");//报错 因为123.0不是int类型
           // in8 = int.Parse("1a");
            double ds2 = double.Parse("123.0");
            Console.WriteLine(in8);

            // 布尔值的转换 不区分大小写  但是单词之间不能有空格
            bool b4 = bool.Parse("TRUE");
            Console.WriteLine(b4);

Covert

Covert类就是专门进行类型转换的类,Convert类提供的方法可以实现各种数据类型之间的转换

            int i1 = 11;
            byte by =Convert.ToByte(i1);
            short sh = Convert.ToInt16(i1);
            //Convert.ToByte() 转byte
            // Convert.ToInt16() 转short
            //Convert.ToUInt16() 转ushort
            //Convert.ToInt32() 转int
            //Convert.ToInt64() 转long
            //Convert.ToUInt64() 转ulong
            Console.WriteLine(by);

练习

  • 做一个用户信息录入的功能

    让用户输入姓名、性别、爱好、国籍,全部输入完成后输出”你的姓名是xx性别是xx,爱好是xx,国籍是xx“

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值