前言
在上一课 中我们通过C#入门程序了解到关于C#的基础知识,这节课我们来感受作为C
家族最大的黑马,在TIOBE榜单 上受欢迎程度未来两个月可能超越java的存在:C#的魅力
重点先知
1、
C#程序
或DLL的源代码是一组类型声明
。
2、对于可执行程序,类型声明中必须
有一个包含 Main 方法的类
。
3、命名空间是一种将相关的类型声明分组并命名的方法。因为程序是一组相关的类型声明
所以通常在你创建的命名空间内部声明程序类型。
写一个C#程序,程序有一个命名空间,命名空间由三个类型声明组成
当你创建一个C#程序时,通常会包含一个主命名空间,该命名空间包含一些类型声明。下面是一个简单的例子,其中包含一个命名空间,该命名空间包含三个不同的类型声明:
using System;
// 声明一个命名空间
namespace MyNamespace
{
// 声明第一个类型
public class MyClass1
{
public void Method1()
{
Console.WriteLine("Method1 from MyClass1");
}
}
// 声明第二个类型
public class MyClass2
{
public void Method2()
{
Console.WriteLine("Method2 from MyClass2");
}
}
// 声明第三个类型
public static class MyStaticClass
{
public static void StaticMethod()
{
Console.WriteLine("StaticMethod from MyStaticClass");
}
}
// 主程序
class Program
{
static void Main()
{
// 创建类型的实例并调用方法
MyClass1 obj1 = new MyClass1();
obj1.Method1();
MyClass2 obj2 = new MyClass2();
obj2.Method2();
// 调用静态类的静态方法
MyStaticClass.StaticMethod();
}
}
}
在这个例子中,我们创建了一个名为 MyNamespace
的命名空间,其中包含了三个不同的类型:MyClass1
、MyClass2
和 MyStaticClass
。主程序位于 Program
类中,它在 Main
方法中创建了这些类型的实例并调用了它们的方法。请注意,静态类的方法可以直接通过类名调用。
C#程序既然是一组类型声明,那么学习 C#就是学习如何创建
和使用类型
。所以,我们首先要了解什么是类型
?
类型是一种模板
想象一下类型就像是创建各种东西的一种模型或蓝图
。这个模型并不是实际的
东西,但它告诉我们如何构建
出具体的对象
,就好像是一份详细说明书。
类型由下面的元素定义
- 名称
- 用于保存数据成员的数据结构
- 一些行为及约束条件。
举两个简单的例子
这两个例子介绍了浮点数类型(float)和字符串类型(string)。浮点数用于表示带有小数部分的数值,而字符串用于存储文本信息。这些数据类型在编程中非常常见,初学者可以通过它们了解不同种类的数据及其用途。
名称 | 数据结构 | 行为及约束条件 |
---|---|---|
浮点数 (float) | 用于表示带有小数部分的数值 存储范围: 约 ±1.5 x 10^(-45) 到 ±3.4 x 10^(38) | 可以进行基本算术运算 适合表示需要小数精度的数值 |
字符串 (string) | 用于存储文本数据,由一系列字符组成 | 可以进行字符串的拼接、比较、截取等操作 字符串是不可变的,一旦创建就不能被修改 适合表示文本信息,如名称、句子等 |
我不太能理解既然字符串是“一旦创建就不能被修改”,为什么字符串又能进行拼接和截取?
抱歉,我可能没有表达得很清楚。让我更详细地解释一下。
在C#中,字符串是不可变的,这意味着一旦创建了字符串,它的内容就不能被修改。例如,如果你有一个字符串 “hello”,你不能直接修改它的某个字符,比如将 “h” 改成 “H”。如果你尝试这样做,实际上会创建一个新的字符串,而不是修改原始的字符串。
但是,对于字符串的操作,如拼接和截取,会创建新的字符串,而不是直接修改原始字符串。这是因为字符串的不可变性。例如:
string greeting = "Hello";
string newGreeting = greeting + ", World!"; // 拼接字符串,创建新字符串
在这个例子中,greeting + ", World!"
创建了一个新的字符串,而原始的 greeting
字符串保持不变。拼接操作并没有直接修改 greeting
,而是生成了一个新的字符串。
同样,截取字符串的操作也是返回一个新的字符串,而不是在原始字符串上进行修改:
string subString = greeting.Substring(0, 3); // 截取字符串,创建新字符串 "Hel"
所以,尽管字符串是不可变的,你仍然可以通过创建新的字符串来执行各种操作,如拼接和截取。这有助于确保原始数据的不可变性。
我们刚刚说到类型就像是创建各种东西的一种模型或蓝图
,那么这种东西是什么呢?
实例化类型
定义:从某个类型模板创建实际的对象,称为实例化该类型
换言之
- 通过实例化类型而创建的对象被称为类型的对象或类型的实例。
- 在C#程序中每个数据项都是某种类型的实例。这些类型可以是语言自带的,也可以是程序员定义的
实例化这两个类型
当你在C#中实例化不同类型时,你通常使用关键字 new 来创建对象。下面是使用 new 关键字实例化浮点数类型(float)和字符串类型(string)的代码示例:
在这个例子中,我们使用 float 关键字
实例化了一个浮点数,并使用 string 关键字
实例化了一个字符串。然后,我们通过Console.WriteLine
将这些实例化的值输出到控制台。请注意,为了区分浮点数类型的字面量,我们在 3.14 的末尾添加了 ‘f’。
像 string、float 这样的类型称为简单类型。这种类型只能存储
一个数据项
。其他的类型可以存储多个数据项。比如数组(array)类型
就可以存储多个同类型
的数据项。这些数据项称为数组元素
。可以通过数字
来引用
这些元素,这些数字称为索引
。
那么在除了这些类型之外,类型声明中还有什么呢?
类型声明中成员的类别
另外一些类型可以包含许多
不同类型的数据项
。这些类型中的数据项个体称为成员
,并且与数组中使用数字来引用成员不同,
数据成员和函数成员
数据成员
:保存了与这个类的对象或整个类相关的数据。函数成员
:执行代码。函数成员定义类型的行为
。
C#程序,一个命名空间中,一个类型声明,包含两个数据成员和两个函数成员
下面是一个简单的C#程序,其中包含一个命名空间、一个类型声明,以及两个数据成员和两个函数成员:
using System;
namespace MyNamespace
{
// 类型声明
public class MyClass
{
// 数据成员
private int myInt;
private string myString;
// 构造函数
public MyClass(int intValue, string stringValue)
{
myInt = intValue;
myString = stringValue;
}
// 函数成员1
public void DisplayValues()
{
Console.WriteLine($"Int Value: {myInt}");
Console.WriteLine($"String Value: {myString}");
}
// 函数成员2
public void UpdateValues(int newIntValue, string newStringValue)
{
myInt = newIntValue;
myString = newStringValue;
}
}
// 主程序
class Program
{
static void Main()
{
// 创建类型的实例
MyClass myObject = new MyClass(42, "Hello");
// 调用函数成员1
myObject.DisplayValues();
// 调用函数成员2
myObject.UpdateValues(99, "World");
// 再次调用函数成员1,显示更新后的值
myObject.DisplayValues();
}
}
}
在这个例子中,命名空间是 MyNamespace
,包含一个类型声明 MyClass
。MyClass
类型有两个私有的数据成员 myInt
和 myString
,以及两个函数成员 DisplayValues
和 UpdateValues
。主程序(Program
类)创建了 MyClass
的一个实例,并通过调用函数成员来展示和更新数据成员的值。
刚刚我们看到的,string和float都属于预定义类型,其实在C#中,共有 16种预定义类型,其中包括13 种简单类型和 3 种非简单类型。
所有预定义类型的名称都由全小写的字母组成。
预定义的简单类型包括以下3 类
- 11种数值类型
- 1、不同长度的有符号和无符号整数类型
- 2、浮点数类型 float 和 double
- 3、一种称为
decimal
的高精度小数
类型。与float 和 double 不同,decimal类型可以准确地表示分数。decimal类型常用于货币的计算
- 一种 Unicode字符类型 char
- 一种布尔类型 bool。bool类型表示布尔值并且必须为 true 或 false
3种非简单类型
- string,它是一个Unicode字符数组
- object,它是所有其他类型的基类
- dynamic,使用动态语言编写的程序集时使用
之前我们已经在这里 聊过了关于类型的概念,在此不再赘述,我们接着往下看
提到预定义类型,同学们是不是很快就想到还有自定义类型
?
用户自定义类型
除了C#提供的 16种预定义类型,还可以创建自己的用户定义类型。有 6种类型可以由用户自己创建,它们是:
- 类类型(class);
- 结构类型(struct);
- 数组类型(array);
- 枚举类型 (enum);
- 委托类型 ( delegate );
- 接口类型(interface)
类型通过类型声明创建,类型声明包含以下信息:
- 1、要创建的类型的种类;
- 2、新类型的名称;
- 3、对类型中每个成员的声明(名称和规格 ),array 和 delegate 类型除外,它们不含有命名成员。
注意
一旦声明了类型,就可以创建和使用这种类型的对象,就像它们是预定义类型一样。使用预定义类型是一个单步过程,简单地实例化对象即可。使用用户自定义类型是一个
两步过程
:必须先声明类型
,然后实例化该类型的对象
。
栈和堆
程序运行时,它的数据必须存储在内存中。一个数据项需要多大的内存、存储在什么地方以及如何存储都依赖于该数据项的类型。
运行中的程序使用两个内存区域来存储数据:栈和堆。
栈
栈是一个内存数组,是一个 LIFO(Last-In First-Out,后进先出)的数据结构。栈存储几种类型的数据:
- 某些类型变量的值;
- 程序当前的执行环境;
- 传递给方法的参数。
系统管理所有的栈操作。作为程序员,你不需要显式地对它做任何事情。但了解栈的基本功能可以更好地了解程序在运行时在做什么,并能更好地了解 C#文档和著作
栈的特征
栈有如下几个普遍特征
- 数据只能从栈的顶端插入和删除。
- 把数据放到栈顶称为入栈(push )
- 从栈顶删除数据称为出栈(pop)。
堆
堆是一块内存区域,在堆里可以分配大块的内存用于存储某种类型的数据对象。与栈不同,堆里的内存能够以任意顺序存人和移除。
虽然程序可以在堆里保存数据,但并不能显式地删除它们。CLR 的自动垃圾收集器在判断出程序的代码将不会再访问某数据项时,会自动清除无主的堆对象。我们因此可以不再操心这项使用其他编程语言时非常容易出错的工作了。
值类型和引用类型
类型被分为两种:值类型和引用类型,这两种类型的对象在内存中的存储方式不同。
- 值类型只需要一段单独的内存,用于存储实际的数据
- 引用类型需要两段内存
- 第一段存储实际的数据,它总是位于堆中
- 第二段是一个引用,指向数据在堆中的存放位置
存储引用类型对象的成员
- 引用类型对象的数据部分始终存放在堆里,
- 值类型对象,或引用类型数据的引用部分可以存放在堆里,也可以存放在栈里,这依赖
于实际环境。
注意:
对于一个引用类型,其实例的数据部分始终存放在堆里。既然两个成员都是对象数据的一部分那么它们都会被存放在堆里无论它们是值类型还是引用类型
C#类型总结
将C#中的类型以横向为预定义类型和用户自定义类型,纵向为值类型和引用类型分类列个表
下面是一个将C#中的类型按照值类型和引用类型、以及预定义类型和用户自定义类型进行分类的表格:
总结
今天我们学习了C#类型的相关知识,其中由于时间原因,对于一些重要的概念我尚没有补充图示,明天有时间补一下,感谢大家的喜爱,喜欢本文的话,给博主点个赞吧!记得收藏加关注哦!道友们的支持就是我更新的源动力!