C#四、详解类型、变量与对象

四、详解类型、变量与对象

在这里插入图片描述

类型

在这里插入图片描述

什么是强类型语言?

即对数据的约束很严格的编程语言

强类型和弱类型语言的比较

  • 不能将高精度的数据,存放在低精度的类型中

    在这里插入图片描述

  • 不能将赋值的结果作为bool值

    在这里插入图片描述

  • 不能将其他类型的数据,简单的转换到bool类型中

    在这里插入图片描述

  • 以上的这几种不能通过的情况,在C和C++中都是可以通过的

    所以C和C++都是比较弱类型语言

强类型语言的特点:保持数据的完整性、保持数据的准确性

JS的动态类型

完全算是弱类型语言

基本上数据类型不受约束

在这里插入图片描述

JavaScript中的 Var 和 C# 中的 Var 是不一样的

JS中的Var相当于任意数据类型,完全可以动态的变换

声明的变量,不受约束,可以装不同类型的数据。

弱类型语言,会更加的灵活。

强类型语言与弱类型语言可以说是各有优点的

C#对弱类型、动态类型的模仿

C#无论什么时候都是强类型的。

但是它为了使自己更加的灵活,对弱类型进行模仿,从而引入了一种机制。叫做dynamic

在这里插入图片描述

通过这种办法,我们获得了类似弱类型、动态类型

初学用不着这个dynamic,当与底层打交道的时候才能发挥它的作用。

类型在C#中的作用

在这里插入图片描述

变量的内存空间、值的范围

查看表格

image-20240119232604996

在这里插入图片描述

在这里插入图片描述

​ float也是32位

程序的静态与动态

程序没有执行即为静态,执行起来即进入动态

编辑和编译代码都是静态的,Debug就是动态的

在编译的时候,编译器就可以拿着类型去检验代码(检验是否合法调用之类的),编译不通过就直接报错

查看基类,和自己的成员

后面只要拿到这个属性就可以通过反射技术访问这个属性的值

只要拿到这个方法,就可以动态的调用它

using System.Reflection;

namespace DataTypeStudy {
    internal class Program {
        static void Main(string[] args) {
            Type myType = typeof(int);
            Console.WriteLine(myType.Name);
            //该类的全称
            Console.WriteLine(myType.FullName);
            //基类的全称
            Console.WriteLine(myType.BaseType.FullName);

            //获取全部属性
            PropertyInfo[] pInfos = myType.GetProperties();
            //获取所有方法
            MethodInfo[] mInfo = myType.GetMethods();
            //输出所有属性
            foreach (var p in pInfos)
            {
                Console.WriteLine(p.Name);
            }
            foreach (var m in mInfo)
            {
                Console.WriteLine(m.Name);
            }

        }
    }
}

此类所允许的操作

数据类型知道自己所允许做的操作

比如,整数除法做出来一定是整数,小数除法做出来一定是小数。

double a = 3.0 / 4.0;//此时计算出来一定是0.75
double b = 3 / 4;//此时计算出来则是0

这就代表着,数据类型是知道自己所允许做的操作的

程序运行时,查看变量在内存中的分配

Stack 栈

  • 栈作用于函数调用

  • 比较小只有1~2M

  • 当函数调用太多就会爆栈

  • 程序有错误,不小心在栈上分配了太多的内存,也会爆

  • 爆栈,Stack overflow,栈溢出

  • 无限递归爆栈

    namespace StackOverflow {
        internal class Program {
            static void Main(string[] args) {
                BadGuy badGuy = new BadGuy();
                badGuy.BadMethod();
            }
        }
        class BadGuy {
            //无限递归调用,就会爆栈
            public void BadMethod() {
                int x = 100;
                this.BadMethod();
            }
        }
    }
    
  • 切过多的内存也会爆掉

    namespace StackOverflow {
        internal class Program {
            static void Main(string[] args) {
                unsafe {
                    int * p = stackalloc int[9999999];
                    /*可以在不安全的模式下,使用指针
                     * stackalloc
                     * 可以分配内存
                     * 切这么多内存会直接爆掉
                     */
                }
            }
        }
    }
    

Heap 堆

  • 用于存储对象

  • 比较大,有几个G

  • 堆不会爆掉,但是会造成浪费即,内存泄露

  • 在C++中,创建对象,如果不去回收就会泄露,因此需要手动的回收

  • 而C#中有垃圾回收机制,当没有使用该东西的时候,会自动回收

  • 通过这个程序,在加上内存检测,就可以直观的看到堆的内存被占用,以及自动释放机制

    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace HeapSimple {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window {
            public MainWindow() {
                InitializeComponent();
            }
            //先消耗一些内存
            List<Window> winList;
            private void Button1_Click(object sender, RoutedEventArgs e) {
                winList = new List<Window>();
    
                for (int i = 0; i < 15000; i++) { 
                    Window w = new Window();
                    winList.Add(w);
                }
                /*
                 * 直接生成15000个win对象
                 */
            }
    
            private void Button2_Click(object sender, RoutedEventArgs e) {
                winList.Clear();
            }
        }
    }
    
  • 在这里插入图片描述

  • 可以直观的看到内存被占用

C#语言的类型系统

在这里插入图片描述

蓝色的关键字,表示都是数据类型,并且是比较常用的数据类型,而且还是基本类型

枚举类型

什么时候用枚举类型?

用户从一个集合当中选择有效值的时候

在这里插入图片描述

例如窗口的状态,只有三种,默认,最大化,最小化。这就是枚举类型

可以对比着Java中的枚举类型来学习

C#语言的类型系统包括引用类型和值类型两种,其中引用类型包括,类、接口、委托,值类型包括,结构体和枚举。所有类型都以Object类型为自己的基类。

变量、对象与内存

在这里插入图片描述

变量名表示(对应着)变量的值在内存中的存储位置

short y = 200;
int x;
x = y;
//这是可以通过的,可以用一个大空间,来存储规定空间小的值

参数变量

//其中的double a, double b 就是值参数
public double Add(double a, double b){
    
}
//当前面加上ref 那么就是引用参数
public double Add(ref double a, double b){
    
}
//当前面加上out 那么就是输出参数
public double Add(out double a, double b){
    
}

变量声明方式

有效的修饰符组合(可有可无) 类型 变量名 初始化器(可有可无)

public static int Amount = 1;
/*
public static 就是有效的修饰符组合
int 是类型
Amount 是变量名
= 1 就是初始化器
*/

变量 = 以变量名所对应的内存地址为起点、以其数据类型所要求的存储空间为长度的一块内存区域

值类型的变量

值类型变量和引用类型变量在内存中的存储方式是不一样的

在这里插入图片描述

在内存中有一块区域是保留给操作系统使用的,当其他程序侵入这块内存,就会使操作系统崩溃

教程视频当中,变量在内存当中的存储方式这部分的讲解很重要

例如:byte 在内存中怎么存储的?

byte b;
b = 100;
//首先在内存中,找一块没有被使用的内存,然后找到该区域的内存编号。因为byte的长度是8个字节,并且无符号位
//所以当b被赋予100这个值的时候,就要把100转换成二进制1100100
//然后存入内存中

在这里插入图片描述

sbyte 有正负之分,所以其二进制的最高位是符号位,1表示负数,0表示正数

二进制存储负数,是将正数,按位取反,最后加1,就是负数

-128就是,128,的二进制形式,按位取反,最后加1,得到的,所以-128的二进制形式是,1000 0000

例子二:ushort

ushort us;
us = 1000;
//1000在二进制中表示,1111101000 有十位,因为是ushort一共有16位
//所以1111101000左边再补6个0

在这里插入图片描述

注意,是从内存编号大的开始存储的

值类型没有实例,或者说,所谓的“实例”是与变量合二为一的

int x = new int();//这样写虽然可以通过,但是我们一般都不这么写
//就是因为实例与变量合二为一了

引用类型的变量与实例

引用类型变量里面存储的数据是对象的内存地址

值类型是按照实际大小来分配内存的。

而引用类型分配内存则和值类型不一样。

系统会给引用类型分配4个字节的位置

例如:

namespace TypeSystem {
    internal class Program {
        static void Main(string[] args) {
            Student st;//此时系统会分配4个字节给st
            st = new Student();
            /*
             * 此时,会在堆中创建实例,
             * 创建实例,它会根据这个类去计算,需要多少的空间
             * 就这个例子中,ID需要4个字节,Score需要2个字节,于是会分配6个字节的位置
             * 这个实例才是真正包含 ID 和 SCore 这两个字段的实体
             * 创建了实例之后,会把这个在堆内存中的地址编号保存在st 中
             * 图中,30000001编号会转换成二进制存在st中
             * 所以我们可以用两个引用变量来引用同一个实例
             * 因为这两个引用变量中都存的是同一个堆中的地址
             */
            Student st2;
            st2 = st;//这个过程就是把st存的那个地址给st2
        }
    }

    class Student {
        uint ID;
        ushort Score;

    }
}

在这里插入图片描述

在这里插入图片描述

一般值类型变量的默认值是0,引用变量的默认值则是null

本地变量如果不赋值,是不允许使用的。这点在C++中则可以使用。

const关键字表示不可改变,当变量被这个关键字修饰时,该变量则只能初始就定义不能先声明在赋值,并且值不可再改变。

装箱与拆箱

装箱

把栈中的数据,封装成堆上的实例

/*
 * 装箱
 * 当obj要引用的值不是堆上的实例,而是栈上的值类型的值的时候
 * 此时会先将栈上面的这个值,封装到堆上的空白内存上的实例,
 * 并且把这个堆上的地址返回给栈上的obj
 * 所以从栈上往堆上搬东西,就叫做装箱,会损失一定的程序的性能
 */
int x = 100;
Object obj;
obj = x;

在这里插入图片描述

拆箱

把堆上的实例,拆成目标数据类型,存储在栈中

/*
 * 装箱
 * 当obj要引用的值不是堆上的实例,而是栈上的值类型的值的时候
 * 此时会先将栈上面的这个值,封装到堆上的空白内存上的实例,
 * 并且把这个堆上的地址返回给栈上的obj
 * 所以从栈上往堆上搬东西,就叫做装箱,会损失一定的程序的性能
 */
int x = 100;
Object obj;
obj = x;

int y = (int)obj;
/*
 * 把堆上面的数据拿到栈上
 * 这个操作就叫做拆箱
 * 把堆上面的数据直接复制到栈里面的y中
 * 
 */

在这里插入图片描述

装箱和拆箱都会损失,程序的性能

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值