C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用

简介:

这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念。现在给大家分享出来我的笔记:

一,.NET平台的重要组成部分都是有哪些

1)FCL (所谓的.NET框架类库)

这些类是微软事先定义好的。

例如当我们新创建一个windows窗体应用程序是,VS会帮我们自动生成下面的代码:

1
2
3
using  System;
using  System.Collections.Generic;
using  System.Text;


这些就是微软事先为程序员定义好的类库。程序员可以直接使用的。

2)CLR (所谓的公共语言运行时)

创建部署.NET程序的必备环境

使用C#,F#,VB等语言都可以来创建.NET应用程序。这时候就需要一个公共语言规范,来把不同的语言解释成.NET FramWork认识的东西。

二,什么是程序集

程序集主要有MSIL组成(所谓的微软中间语言,主要由dll文件组成)

不同编程语言程序被.NET FrameWork编译成程序集(dll文件),当程序需要被执行时,通过CLR中的JIT(及时编译器)编译成本地代码,并将指令发送给CPU执行。

程序集一般有两种:.dll和.exe文件(但是不是所有的dll和exe都叫程序集)

比如:我们在同一个解决方案下,建立多个应用程序或者类库文件。这些程序或者类库编译后就会变成不同的程序集。他们之间是相互独立的。之间如果想要相互访问,需要添加引用。

三,Parse转换和Convert转换的区别

1)Parse转换

①Parse转换只能转换字符串

②自变量是指定的数据类型才可以转换成功

下面的是.NET Reflector编译的源代码

181635147529.png

2)Convert转换

①可以转换其他类型(如:类)

②与Parse的区别就是,转换前会对被转换的对象进行判断,如果对象为null则会转换失败

181635325480.png

下面是实例源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class  Program
     {
         static  void  Main( string [] args)
         {
             string  a = Console.ReadLine();
             //Parse只可以转换字符串
             int  b = Int32.Parse(a);
             //Convert可以转换类等对象
             ParseNumber parNum =  new  ParseNumber();
             //这种写法编译器会报错
             //int b = Int32.Parse(parNum);
             int  c = Convert.ToInt32(parNum);
             Console.WriteLine(b);
             Console.WriteLine(b.GetType());
             Console.ReadKey();
         }
     }
     class  ParseNumber
     {
         private  int  nunm;
         public  int  Num {  get set ; }
     }


四,数据类型的存储位置

1)存储在栈中的数据类型

所有数值类型,char,bool,枚举,结构体

2)存储在堆中

string,数组,类

管这些类型,他们的变量的声明都是保存在栈里,真实的对象保存在堆里面,栈里面的变量存储打的是对象的地址。

下面以数组来简单说一下这个问题:

1
2
//声明一个一维数组
    int [] arr =  new  int [4];


那么这个表达式的执行顺序是什么呢?

①首先程序会在栈中开辟一段名为arr的int[]类型的空间

②然后在堆中开辟一个int[]对象,再该对象中会有4块连续的内存空间

③堆中的对象返回类型为地址,即new  int[4]表达式返回的是地址

示意图如下:

wKioL1LbWISit23bAADIBDdUrPU294.jpg

五,C#方法调用

1)在C#中我们可以给参数传递默认值,所以当我们调用这个方法的时候,可以不给这个参数传递值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static  void  Main( string [] args)
         {
             //声明一个一维数组
             int [] arr =  new  int [4];
             Program pro =  new  Program();
        //直接调用,没有传递参数值
             pro.para();
                                                                                          
         }
         public  void  para( int  i=5)
         {
             Console.WriteLine(i);
             Console.ReadKey();
         }


2)带默认参数的方法,默认值必须放在最右侧

下面的写法编译器会报错

181712446112.png

3)方法的可变参数

①可变参数被Params

②Params只能用来修饰一维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static  void  Main( string [] args)
         {
             //声明一个一维数组
             int [] arr =  new  int [4];
             for  ( int  i = 0; i < arr.Length; i++)
             {
                 arr[i] = i;
             }
             Program pro =  new  Program();
             pro.para();
             //传递一位数组arr
             pro.param(arr);
             Console.ReadKey();
         }
         //params用来修饰一维数组
         public  void  param( params  int [] arr)
         {
             foreach  ( var  item  in  arr)
             {
                 Console.WriteLine(item);
             }
         }


③给可变参数赋值的时候可以直接传递数组元素

1
2
3
4
5
6
7
8
9
10
11
12
13
//声明一个一维数组
             int [] arr =  new  int [4];
             for  ( int  i = 0; i < arr.Length; i++)
             {
                 arr[i] = i;
             }
             Program pro =  new  Program();
             pro.para();
             //传递一位数组arr
             pro.param(arr);
             //直接传递数组元素,调用时会自动将这些数封装成数组,并将数组传递
             pro.param(0, 1, 2, 3);
             Console.ReadKey();


④与默认参数一样,可变参数的声明必须放在方法参数的最后

181728029554.png

4)方法的out和ref参数

①out参数侧重于输出,必须在方法内对其赋值

如下图的声明编译器会报错

181732009556.png

正确的使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static  void  Main( string [] args)
         {
             //声明参数m
             int  m=0;
             Program pro =  new  Program();
             //传递参数m,必须带有out参数标识
             pro.outPara(  out  m);
             Console.WriteLine(m);
             Console.ReadKey();
         }
         //out参数侧重于输出,必须在方法内对其赋值
         public  void  outPara( out  int  i)
         {
             //方法内部必须对out参数进行赋值
             i=5;
         }


②ref参数侧重于修改,但是也可以不修改参数的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static  void  Main( string [] args)
         {
             //声明参数m
             int  m=0;
             Program pro =  new  Program();
             //传递参数m,必须带有out参数标识
             pro.outPara(  out  m);
        //ref参数传递之前必须对其赋值,因为在方法内部可能会用到该参数
             pro.refPara( ref  m);
             //Console.WriteLine(m);
             Console.ReadKey();
         }
         //
         public  void  refPara( ref  int  i)
         {
             Console.WriteLine( "可以不对参数i进行任何操作!" );
         }


输出结果如下:

181743132674.png


六,属性易混淆点辨别

①属性本身不存值,值是存在这个属性所封装的字段里面

1
2
3
4
5
6
7
8
9
10
11
12
class  Study
     {
         private  int  nID;
         //属性的值存储在封装的字段里面
         public  int  NID
         {
                                                              
             get  return  nID; }
             //这里我们给属性赋值
             set  { nID = value; }
         }
     }


通过访问属性字段获取字段的值

1
2
3
Study stu =  new  Study();
            //通过访问属性字段获取字段的值
            int  nID = stu.NID;


②属性的返回值类型和字段的值类型没有关系

1
2
3
4
5
6
7
8
//属性的值类型为bool
         private  bool  gender;
         //字段的返回类型为string
         public  string  Gender
         {
             get { return  gender== true ? "男" : "女" ;}
             set {gender =value== "男" ? true : false ;}
         }


属性的返回值类型决定了get返回值的类型和set参数的类型

1
2
3
4
5
6
7
8
9
10
//属性的值类型为bool
         private  bool  gender;
         //字段的返回类型为string
         public  string  Gender
         {
             //get的返回值类型为bool
             get { return  gender== true ? "男" : "女" ;}
             //set参数类型为bool
             set {gender =value== "男" ? true : false ;}
         }


③自动属性到底是怎么回事?

看如下的代码:

1
2
3
4
5
6
7
private  string  strName;
         //自动属性封装strName
         public  string  StrName
         {
             get ;
             set ;
         }


这就是所谓的自动属性封装字段。在非自动属性中,程序默认的会有value值来给字段赋值,但是在自动属性中是怎么赋值的呢?

我们使用.NET Reflector反编译来看源代码:

这是我们封转的属性代码:

181825445334.png

反编译set函数源代码:

181829264550.png

我们可以看到.NET会默认为我们的程序生成一个成员变量<StrName>k__BackingField

get函数的源代码:

181827133148.png

返回的也是该成员变量;


那么什么时候可以使用自动属性呢?

如果对一个字段取值和赋值的时候没有任何逻辑验证并且可读可写的时候,就可以使用自动属性。

七,C#类声明易混淆知识点

①首先给大家说明一个问题就是,文件名和类名必须是一样的么(就是我们在创建类的时候要命明,这个时候会默认的生成一样的类名称)?

如图所示

181837570339.png

这个是必须的么?

我们尝试修改类名称为ChildName,然后访问类

181843274557.png

可以看到我们要访问类,需要通过类名称访问而与文件名没有关系。

②类表达式的执行顺序和其意义

  Study stu = new Study();

编译器执行代码的时候,

首先会先在栈中开辟一块类型为Study的内存空间放置变量stu

然后在堆中创建该变量的对象

然后调用该对象的构造函数,并且返回该对象在堆中的地址。

181854018149.png


好吧,到这里,这次的分享就到此结束了。大家如果阅读的过程中有什么问题,可以跟我留言交流。







     本文转自yisuowushinian 51CTO博客,原文链接:http://blog.51cto.com/yisuowushinian/1352834,如需转载请自行联系原作者




相关文章
|
2月前
|
存储 C# 索引
C# 一分钟浅谈:变量与数据类型简介
【9月更文挑战第1天】在 C# 编程中,了解变量与数据类型至关重要。本文详细介绍了 C# 中的值类型(如整数、浮点数、布尔值等)和引用类型(如类、接口、数组、字符串)。通过示例代码展示了变量的声明与使用方法,并针对数据类型转换错误、变量未初始化及数值溢出等常见问题提供了解决方案。正确选择数据类型不仅能提升程序性能,还可避免潜在错误,有助于编写高质量代码。
130 47
|
2天前
|
开发框架 .NET API
以C#一分钟浅谈:GraphQL 数据类型与查询
本文从C#开发者的角度介绍了GraphQL的基本概念、核心组件及其实现方法。GraphQL由Facebook开发,允许客户端精确请求所需数据,提高应用性能。文章详细讲解了如何在C#中使用`GraphQL.NET`库创建Schema、配置ASP.NET Core,并讨论了GraphQL的数据类型及常见问题与解决方案。通过本文,C#开发者可以更好地理解并应用GraphQL,构建高效、灵活的API。
83 64
|
2月前
|
C# 数据安全/隐私保护
C# 一分钟浅谈:类与对象的概念理解
【9月更文挑战第2天】本文从零开始详细介绍了C#中的类与对象概念。类作为一种自定义数据类型,定义了对象的属性和方法;对象则是类的实例,拥有独立的状态。通过具体代码示例,如定义 `Person` 类及其实例化过程,帮助读者更好地理解和应用这两个核心概念。此外,还总结了常见的问题及解决方法,为编写高质量的面向对象程序奠定基础。
27 2
|
3月前
|
Java C#
C# 和 java 基本数据类型
C# 和 java 基本数据类型
22 0
|
3月前
|
C# 计算机视觉
C#中out关键字
C#中out关键字
62 0
|
3月前
|
编译器 C# 计算机视觉
C#中的ref关键字
C#中的ref关键字
49 0
|
5月前
|
存储 C# 开发者
C# 编程基础:注释、变量、常量、数据类型和自定义类型
C# 编程基础:注释、变量、常量、数据类型和自定义类型
|
5月前
|
存储 Java 编译器
C# 变量与参数详解
C# 变量与参数详解
|
5月前
|
开发框架 .NET 程序员
掌握C#语言的精髓:基础知识与实用技能详解(数据类型与变量+ 条件与循环+函数与模块+LINQ+异常+OOP)
掌握C#语言的精髓:基础知识与实用技能详解(数据类型与变量+ 条件与循环+函数与模块+LINQ+异常+OOP)
33 0
|
6月前
|
C# Python
C#如何用最简单方法调用Python?
本文介绍了如何在C#中通过命令行调用Python脚本,以解决Python.NET的版本兼容性和配置难题。方法是利用`System.Diagnostics.Process`类启动Python解释器并传递脚本路径。首先确保安装Python 3.0+,创建.NET控制台程序和Python测试脚本。然后,C#代码通过指定Python路径和脚本位置启动进程,重定向输出并读取结果。这种方法简单、依赖少,适合初学者,但可能牺牲性能,不适合频繁交互和处理大量数据。
110 1