【学习笔记】C#基础 - 由C/C++衍生出来的面向对象的编程语言

2023-09-14 17:46:02

一、程序结构

1、基本语法

using System; // C#程序第一条语句都是 using System; 
// using 关键字(可多个):在程序中包含命名空间

namespace RectangleApplication // namespace 声明(可多个):包含一系列的类
{
    class Rectangle // class 声明(可多个):包含程序数据和方法声明
    {
        double width; // 变量声明:类的属性或数据成员,用于存储数据
        double length;

        public void Acceptdetails() // 函数声明:一系列执行指定任务的语句
        {
            width = 3.5;
            length = 4.5;
        }
        public double GetArea()
        {
            return width * length;
        }
        public void Display()
        {
            Console.WriteLine("Length:{0}", length);
            Console.WriteLine("Width: {0}", width);
            Console.WriteLine("Area: {0}", GetArea());
        }
    }
    class ExecuteRectangle // 实例化 Rectangle 的类
    {
        static void Main(string[] args) // Main 方法:定义类的动作(所有 C# 程序的入口点,程序执行从此开始)
        {
            Rectangle r = new Rectangle(); // 实例化 Rectangle
            r.Acceptdetails();
            r.Display();
            Console.ReadLine(); // 输出内容
        }
    }
}

2、类的命名

  1. 必须以 A-Z / _ / @ 开头,不能是数字,之后可以跟 A-Z0-9_@
  2. 不能包含任何空格或特殊符号,比如 ?-+!#%^&*()[]{}.;:"'/\
  3. 不能与关键字同名(除非添加 @ 前缀,@不作为标识符本身的一部分),不能与类库同名
  4. 必须区分大小写(PascalCase命名法)

3、关键字

  • 保留关键字
    abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in in(generic modifier) int interface internal is lock long namespace new null object operator out out(generic modifier) override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual void volatile while

  • 上下文关键字
    add alias ascending descending dynamic from get global group into join let orderby partial(type) partial(method) remove select set

二、数据类型

1、值(Value)类型

直接包含数据,当声明一个值类型时,系统分配内存来存储值

类型描述范围默认值
bool布尔值True 或 FalseFalse
byte8 位无符号整数0 到 2550
char16 位 Unicode 字符U +0000 到 U +ffff'\0'
decimal128 位精确的十进制值,28-29 有效位数(-7.9 x 1028 到 7.9 x 1028) / 100 到 28 0.0M
double64 位双精度浮点型(+/-)5.0 x 10-324 到 (+/-)1.7 x 103080.0D
float32 位单精度浮点型-3.4 x 1038 到 + 3.4 x 10380.0F
int32 位有符号整数类型-2,147,483,648 到 2,147,483,6470
long64 位有符号整数类型-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 0L
sbyte8 位有符号整数类型-128 到 1270
short16 位有符号整数类型-32,768 到 32,7670
uint32 位无符号整数类型0 到 4,294,967,2950
ulong64 位无符号整数类型0 到 18,446,744,073,709,551,6150
ushort16 位无符号整数类型0 到 65,5350
int i = 75;
float f = 53.005f;
double d = 2345.7652;
bool b = true;

Console.WriteLine("Size of int: {0}", sizeof(int)); // 获取任何机器上 int 类型的存储尺寸

2、引用(Reference)类型

不包含实际数据,仅包含对变量的引用(指向一个内存位置)

  • 对象类型object:通用类型系统中所有数据类型的终极基类,可以被分配任何其他数据类型的值(分配值之前需先进行类型转换)
int i = 10;
object obj = i; // 装箱:将一个值类型转换为对象类型
int j = (int)obj; // 拆箱:将一个对象类型转换为值类型

// 拆箱时强转的值类型,应与装箱时的值类型一致

装箱:在堆中自动创建一个对象实例,然后将该值复制到新对象内
此处输入图片的描述

拆箱:检查是否是给定值类型的一个装箱值,然后将该值从实例复制到值类型变量中
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 动态类型dynamic:存储任何类型的值,动态类型变量的类型检查是在运行时发生的
/* 声明动态类型的语法 */
dynamic <variable_name> = value;

dynamic d = 20;
  • 字符串类型string:允许给变量分配任何字符串值(两种分配形式:引号和 @引号)
/* 声明字符串类型的语法 */
string <variable_name> = value;

String str = "runoob.com";
string str = @"C:\Windows"; // 字符串前加 @ 可取消字符串中\的转义,将内容原样输出
string str = @"<script type=""text/javascript"">
    <!--
    -->
</script>"; // @字符串内容可换行书写,换行符及缩进空格都计算在字符串长度之内

3、指针(Pointer)类型

存储另一种类型的内存地址

/* 声明指针类型的语法 */
type* identifier;

char* cptr;

4、无(Void)类型

对定义函数的参数类型、返回值、函数中指针类型进行声明

public class Void Test{} // 无返回值
void* identifier; // 无类型指针:可以指向任何类型的数据

// void几乎只有“注释”和限制程序的作用,定义一个void变量没有意义

使用说明:

  1. 如果函数没有返回值,那么应声明为void类型
  2. 如果函数无参数,那么应声明其参数为void
  3. 小心使用void指针类型
  4. 如果函数的参数可以是任意类型指针,那么应声明其参数为void *
  5. void不能代表一个真实的变量

三、类型转换

  • 隐式类型转换:以安全方式进行转换,不会导致数据丢失
  • 显式类型转换:强制类型转换,需要强制转换运算符,并且会造成数据丢失
double d = 5673.74;
int i;
i = (int)d; // 强制转换 double 为 int,输出 5673
方法描述
ToBoolean如果可能的话,把类型转换为布尔型
ToByte把类型转换为字节类型
ToChar如果可能的话,把类型转换为单个 Unicode 字符类型
ToDateTime把类型(整数或字符串类型)转换为 日期-时间 结构
ToDecimal把浮点型或整数类型转换为十进制类型
ToDouble把类型转换为双精度浮点型
ToInt16把类型转换为 16 位整数类型
ToInt32把类型转换为 32 位整数类型
ToInt64把类型转换为 64 位整数类型
ToSbyte把类型转换为有符号字节类型
ToSingle把类型转换为小浮点数类型
ToString把类型转换为字符串类型
ToType把类型转换为指定类型
ToUInt16把类型转换为 16 位无符号整数类型
ToUInt32把类型转换为 32 位无符号整数类型
ToUInt64把类型转换为 64 位无符号整数类型
int i = 75;
float f = 53.005f;
double d = 2345.7652;
bool b = true;

Console.WriteLine(i.ToString()); // 输出 "75"
Console.WriteLine(f.ToString()); // 输出 "53.005"
Console.WriteLine(d.ToString()); // 输出 "2345.7652"
Console.WriteLine(b.ToString()); // 输出 "True"

四、变量

变量只是一个供程序操作的存储区的名字;每个变量都有一个特定的类型,类型决定了变量的内存大小和布局;范围内的值可以存储在内存中,可以对变量进行一系列操作

类型举例
整数类型sbyte、byte、short、ushort、int、uint、long、ulong 和 char
浮点型float 和 double
十进制类型decimal
布尔类型true 或 false 值,指定的值
空类型可为空值的数据类型

1、变量定义

/* 变量定义的语法 */
<data_type> <variable_list>;

// data_type:一个有效的数据类型
// variable_list:由一个或多个用逗号分隔的标识符名称组成

int i, j, k;
char c, ch;
float f, salary;
double d;

2、变量初始化

/* 变量初始化的语法 */
variable_name = value;
<data_type> <variable_name> = value;

byte d = 3, f = 5;
double pi = 3.14159;
char x = 'x';

int num;
num = Convert.ToInt32(Console.ReadLine());

// ReadLine():接收来自用户的输入,并把它存储到一个变量中
// Convert.ToInt32():把用户输入的数据转换为 int 数据类型

3、两种表达式

  • lvalue 表达式:可以出现在赋值语句的左边或右边
  • rvalue 表达式:可以出现在赋值语句的右边,不能出现在赋值语句的左边

五、常量

常量是固定值,程序执行期间不会改变;常量可以是任何基本数据类型,可以被当作常规的变量,只是它们的值在定义后不能被修改

/* 定义常量的语法 */
const <data_type> <constant_name> = value;

const int c1 = 5;

1、整数常量

85 // 十进制
0213 // 八进制
0x4b // 十六进制
30 // 整型
30u // 无符号整型
30l // 长整型
30ul // 无符号长整型
前缀含义
0x、0X十六进制
0八进制
(无)十进制
后缀(可组合,不可重复)含义
U、uunsigned(无符号)
L、llong(长)

2、浮点常量

3.14159 // 小数形式:必须包含小数点或指数,或同时包含两者
314159E-5L // 指数形式:必须包含整数或小数,或同时包含两者,有符号的指数用e或E表示

3、字符常量

'x' // 普通字符
'\t' // 转义序列
'\u02C0' // 通用字符
转义序列含义
\\\ 字符
\'' 字符
\"" 字符
\?? 字符
\aAlert 或 bell
\b退格键(Backspace)
\f换页符(Form feed)
\n换行符(Newline)
\r回车
\t水平制表符 tab
\v垂直制表符 tab
\ooo一到三位的八进制数
\xhh . . .一个或多个数字的十六进制数

4、字符串常量

string a = "hello, world"; // hello, world
string b = @"hello, world"; // hello, world
string c = "hello \t world"; // hello     world
string d = @"hello \t world"; // hello \t world
string e = "Joe said \"Hello\" to me"; // Joe said "Hello" to me
string f = @"Joe said ""Hello"" to me"; // Joe said "Hello" to me
string g = "\\\\server\\share\\file.txt"; // \\server\share\file.txt
string h = @"\\server\share\file.txt"; // \\server\share\file.txt
string i = "one\r\ntwo\r\nthree";
string j = @"one
two
three";

六、运算符

1、算术运算符

运算符描述实例( A = 10, B = 20)
+把两个操作数相加A + B → 30
-从第一个操作数中减去第二个操作数A - B → -10
*把两个操作数相乘A * B → 200
/分子除以分母B / A → 2
%取模运算符,整除后的余数B % A → 0
++自增运算符,整数值增加 1A++ → 11
--自减运算符,整数值减少 1A-- → 9
int a = 1;
int b;

b = a++; // 先将a赋值给b,再对a进行自增运算:a → 2, b → 1
b = ++a; // 先将a进行自增运算,再将a赋值给b:a → 2, b → 2
b = a--; // 先将a赋值给b,再对a进行自减运算:a → 0, b → 1
b = --a; // 先将a进行自减运算,再将a赋值给b:a → 0, b → 0

2、关系运算符

运算符描述实例( A = 10, B = 20)
==检查两个操作数的值是否相等,如果相等则条件为真A == B → false
!=检查两个操作数的值是否相等,如果不相等则条件为真A != B → true
>检查左操作数的值是否大于右操作数的值,如果是则条件为真A > B → false
<检查左操作数的值是否小于右操作数的值,如果是则条件为真A < B → true
>=检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真A >= B → false
<=检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真A <= B → true

3、逻辑运算符

运算符描述实例( A = true, B = false)
&&逻辑与运算符:如果两个操作数都非零,则条件为真A && B → false
||逻辑或运算符:如果两个操作数中有任意一个非零,则条件为真。A || B → true
!逻辑非运算符:用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假!(A && B) → true

4、位运算符

将条件值换算为二进制数后进行位运算,再将结果换算回去得出最终结果

运算符描述实例( A = 60, B = 13)
&如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中A & B → 12(0000 1100)
|如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中A | B → 61(0011 1101)
^如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中A ^ B → 49(0011 0001)
~按位取反运算符是一元运算符,具有"翻转"位效果,即0变成1,1变成0,包括符号位~A → -61(1100 0011)
<<二进制左移运算符。左操作数的值向左移动右操作数指定的位数 A << 2 → 240(1111 0000)
>>二进制右移运算符。左操作数的值向右移动右操作数指定的位数 A >> 2 → 15(0000 1111)

5、赋值运算符

运算符描述实例
=简单的赋值运算符,把右边操作数的值赋给左边操作数 C = A + B 将把 A + B 的值赋给 C
+=加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 C += A 相当于 C = C + A
-=减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 C -= A 相当于 C = C - A
*=乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 C *= A 相当于 C = C * A
/=除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 C /= A 相当于 C = C / A
%=求模且赋值运算符,求两个操作数的模赋值给左边操作数 C %= A 相当于 C = C % A
<<=左移且赋值运算符 C <<= 2 等同于 C = C << 2
>>=右移且赋值运算符 C >>= 2 等同于 C = C >> 2
&=按位与且赋值运算符 C &= 2 等同于 C = C & 2
^=按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2
|=按位或且赋值运算符 C |= 2 等同于 C = C | 2

6、其他运算符

运算符描述实例
sizeof()返回数据类型的大小sizeof(int)
typeof()返回 class 的类型typeof(StreamReader)
&返回变量的地址&a
*变量的指针*a
? :条件表达式c ? a : b
is判断对象是否为某一类型If(Ford is Car)
as强制转换,即使转换失败也不会抛出异常Object obj = new StringReader("Hello");
StringReader r = obj as StringReader;

运算符优先级:
() [] -> . ++ - -
+ - ! ~ ++ - - (type)* & sizeof()
* / %
+ -
<< >>
< <= > >=
== !=
&
^
|
&&
||
?:
= += -= *= /= %=>>= <<= &= ^= |=
,

七、判断

  • if 语句
if (boolean_expression)
{
   /* 如果布尔表达式为真将执行的语句 */
}
  • if…else 语句
if (boolean_expression)
{
   /* 如果布尔表达式为真将执行的语句 */
}
else
{
  /* 如果布尔表达式为假将执行的语句 */
}

// 条件运算符可替代 if...else 语句:
Exp1 ? Exp2 : Exp3;

// if...else if...else 语句:
if (boolean_expression 1)
{
   /* 当布尔表达式 1 为真时执行 */
}
else if (boolean_expression 2)
{
   /* 当布尔表达式 2 为真时执行 */
}
else if (boolean_expression 3)
{
   /* 当布尔表达式 3 为真时执行 */
}
else 
{
   /* 当上面条件都不为真时执行 */
}

// 另一种写法:
if (a == true)
    x = 1; // 若不书写花括号{},则默认只第一条语句由if执行
    y = 2; // 之后的语句则在if语句之后执行
    z = 3;
  • 嵌套 if 语句
if (boolean_expression 1)
{
   /* 当布尔表达式 1 为真时执行 */
   if (boolean_expression 2)
   {
      /* 当布尔表达式 2 为真时执行 */
   }
}
  • switch 语句
switch (expression){
    case constant-expression  :
       statement(s);
       break; 
    case constant-expression  :
       statement(s);
       break; 
  
    /* 您可以有任意数量的 case 语句 */
    default : /* 可选的 */
       statement(s);
       break; 
}

switch语句规则:

  1. expression 必须是一个整型或枚举类型,或者是一个 class 类型,其中 class 有一个单一的转换函数将其转换为整型或枚举类型。
  2. 在一个 switch 中可以有任意数量的 case 语句,每个 case 后跟一个要比较的值和一个冒号。
  3. case 的 constant-expression 必须与 switch 中的变量具有相同的数据类型,且必须是一个常量。
  4. 当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句时switch 终止,控制流将跳转到 switch 语句后的下一行。
  5. 如果 case 语句中有处理语句,则必须包含 break 或其他跳转语句;如果 case 语句为空,则可以不包含 break,控制流将会继续后续的 case,直到遇到 break 为止。
  6. 一个 switch 语句结尾可以有一个可选的 default case,用于在上面所有 case 都不为真时执行一个任务,default case 中的 break 语句不是必需的。
  7. C# 不支持从一个 case 标签显式贯穿到另一个 case 标签,如需要,可以使用 goto 一个 switch-case 或 goto default。
  • 嵌套 switch 语句
switch (ch1)
{
   case 'A':
      printf("这个 A 是外部 switch 的一部分" );
      switch (ch2)
      {
         case 'A':
            printf("这个 A 是内部 switch 的一部分" );
            break;
         case 'B': /* 内部 B case 代码 */
      }
      break;
   case 'B': /* 外部 B case 代码 */
}

八、循环

1、循环类型

  • while 循环
while (condition) // condition 可以是任意的表达式,当为任意非零值时都为真
{
   statement(s); // 可以是一个单独的语句,也可以是几个语句组成的代码块
}
  • for/foreach 循环
/* for 循环语法 */
for (init; condition; increment)
{
   statement(s);
}

/* foreach 循环语法 */
foreach (type objName in collection/Array)
{
   statement(s);
}

int[] fibarray = new int[] {0, 1, 1, 2, 3, 5, 8, 13};
foreach (int element in fibarray)
{
    System.Console.WriteLine(element);
}

for循环控制流:

  1. init 会首先被执行,且只会执行一次;允许声明并初始化任何循环控制变量,也可以只写一个分号。
  2. 判断 condition,如果为真则执行循环主体,如果为假则直接跳到紧接着 for 循环的下一条语句。
  3. 执行完 for 循环主体后,控制流会跳回 increment 语句,允许更新循环控制变量,也可以留空。
  4. 重复判断 condition,直到条件变为假时,循环终止。
  • do…while 循环
do
{
   statement(s);
} while (condition);

do…while循环控制流:

  1. 条件表达式出现在循环的尾部,所以循环中的 statement(s) 会在条件被测试之前至少执行一次。
  2. 如果 condition 为真,控制流会跳转回上面的 do,重新执行循环中的 statement(s)。
  3. 重复判断 condition,直到条件变为假时,循环终止。
  • 嵌套循环
/* 嵌套 for 循环语法 */
for (init; condition; increment)
{
   for (init; condition; increment)
   {
      statement(s);
   }
   statement(s);
}

/* 嵌套 while 循环语法 */
while (condition)
{
   while (condition)
   {
      statement(s);
   }
   statement(s);
}

/* 嵌套 do...while 循环语法 */
do
{
   statement(s);
   do
   {
      statement(s);
   } while (condition);

} while (condition);

2、循环控制语句

用于更改执行的正常序列,当执行离开一个范围时,所有在该范围中创建的自动对象都会被销毁

  • break 语句
循环作用
switchbreak 用于终止一个 case
其他break 会终止整个循环,且程序流将继续执行紧接着循环的下一条语句
int a = 10;
while (a < 20)
{
    a++;
    if (a > 15)
    {
        break; // a>15时终止while循环
    }
}
  • continue 语句
循环作用
forcontinue 语句会导致执行条件测试和循环增量部分
while、do…whilecontinue 语句会导致程序控制回到条件测试上
其他continue 会跳过当前循环中的代码,强迫开始下一次循环
int a = 10;
do
{
    if (a == 15)
    {
        a = a + 1;
        continue; // a==15时跳过这次迭代,继续循环
    }
    a++;
} while (a < 20);

3、无限循环

如果条件永远不为假,则循环将变成无限循环

for (; ; ) // 当条件表达式不存在时,它被假设为真
{
    Console.WriteLine("陷入死循环啦!");
}

九、封装

把一个或多个项目封闭在一个物理的或者逻辑的包中,通过访问修饰符设置使用者的访问权限,防止对实现细节的访问

访问修饰符访问权限
public所有对象都可以访问
protected internal访问限于当前程序集或派生自包含类的类型
internal同一程序集内的对象可以访问
protected只有该类对象及其子类对象可以访问
private对象本身在对象内部可以访问

此处输入图片的描述

1、Public 访问修饰符

允许一个类将其成员变量和成员函数暴露给所有函数和对象,当前程序集(项目)及外部程序集(项目)都可以访问

namespace RectangleApplication
{
    class Rectangle
    {
        public double length;
        public double width;
        public double GetArea()
        {
            return length * width;
        }
        public void Display()
        {
            Console.WriteLine("长度: {0}", length);
            Console.WriteLine("宽度: {0}", width);
            Console.WriteLine("面积: {0}", GetArea());
        }
        /* 成员变量及函数被声明为 public,故可被所有函数及类的实例访问 */
    }

    class ExecuteRectangle
    {
        static void Main(string[] args)
        {
            Rectangle r = new Rectangle();
            r.length = 4.5;
            r.width = 3.5;
            r.Display();
            Console.ReadLine();
        }
    }
}

2、Protected Internal 访问修饰符

允许在本类、派生类或者包含该类的程序集中访问,用于实现继承

3、Internal 访问修饰符

允许一个类将其成员变量和成员函数暴露给当前程序集中的其他函数和对象,外部程序集(项目)不可访问

namespace RectangleApplication
{
    class Rectangle
    {
        internal double length;
        internal double width;
        /* 成员变量被声明为 internal,故可被当前程序集的函数访问,不可被外部访问 */
        
        double GetArea() // 成员函数声明时不带有任何访问修饰符,默认为 private
        {
            return length * width;
        }
        public void Display()
        {
            Console.WriteLine("长度: {0}", length);
            Console.WriteLine("宽度: {0}", width);
            Console.WriteLine("面积: {0}", GetArea());
        }
    }
    
    class ExecuteRectangle
    {
        static void Main(string[] args)
        {
            Rectangle r = new Rectangle();
            r.length = 4.5;
            r.width = 3.5;
            r.Display();
            Console.ReadLine();
        }
    }
}

4、Protected 访问修饰符

允许子类访问它的基类的成员变量和成员函数,有助于实现继承

5、Private 访问修饰符

允许一个类将其成员变量和成员函数对其他的函数和对象进行隐藏,只有当前类的成员函数可以访问(类的实例不能访问)

namespace RectangleApplication
{
    class Rectangle
    {
        private double length;
        private double width;
        /* 成员变量被声明为 private,故可被当前类的成员函数访问,不可被类外访问 */
        
        public void Acceptdetails()
        {
            Console.WriteLine("请输入长度:");
            length = Convert.ToDouble(Console.ReadLine());
            Console.WriteLine("请输入宽度:");
            width = Convert.ToDouble(Console.ReadLine());
        }
        public double GetArea()
        {
            return length * width;
        }
        public void Display()
        {
            Console.WriteLine("长度: {0}", length);
            Console.WriteLine("宽度: {0}", width);
            Console.WriteLine("面积: {0}", GetArea());
        }
    }
    
    class ExecuteRectangle
    {
        static void Main(string[] args)
        {
            Rectangle r = new Rectangle();
            r.Acceptdetails();
            r.Display();
            Console.ReadLine();
        }
    }
}

注:如果类成员没有指定访问修饰符,则默认为 private

十、方法

1、定义及调用方法

/* 定义方法的语法 */
<Access Specifier> <Return Type> <Method Name>(Parameter List)
{
   Method Body
}

// Access Specifier:访问修饰符,声明变量或方法对于另一个类的可见性
// Return type:方法返回的值的数据类型,若无返回值则类型为 void
// Method name:方法名称,标识符唯一,且大小写敏感
// Parameter list:参数列表,用来传递和接收方法的数据
// Method body:方法主体,包含了完成任务所需的指令集

class NumberManipul
{
    /* 定义方法:访问修饰符 public,返回类型 int,方法名 FindMax,以及参数和主体 */
    public int FindMax(int num1, int num2)
    {
        int result;
        /* 若if内只执行一条语句,可以省略花括号{} */
        if (num1 > num2)
            result = num1;
        else
            result = num2;
            
        return result;
    }
    
    static void Main(string[] args)
    {
        int a = 100;
        int b = 200;
        int ret;
        NumberManipul n = new NumberManipul(); // 实例化 NumberManipul 类
        ret = n.FindMax(a, b); // 调用 FindMax 方法
        Console.WriteLine("最大值是: {0}", ret );
        Console.ReadLine();
    }
}

2、递归函数

一个方法可以自我调用,这就是所谓的递归

class NumberManipulator
{
    public int factorial(int num)
    {
        int result;
        if (num == 1)
        {
            return 1;
        }
        else
        {
            result = factorial(num - 1) * num; // 调用自身并迭代参数以实现递归
            return result;
        }
    }
    
    static void Main(string[] args)
    {
        NumberManipulator n = new NumberManipulator();
        Console.WriteLine("6 的阶乘是: {0}", n.factorial(6));
        Console.WriteLine("7 的阶乘是: {0}", n.factorial(7));
        Console.WriteLine("8 的阶乘是: {0}", n.factorial(8));
        Console.ReadLine();
    }
}

3、参数传递

  • 按值传递参数(默认方式)

当调用一个方法时,会为每个值参数创建一个新的存储位置;实际参数的值会复制给形参,实参和形参使用的是两个不同内存中的值,所以,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全

class NumberManipulator
{
    public void swap(int x, int y)
    {
        int temp;
        temp = x; // 保存 x 的值
        x = y; // 把 y 赋值给 x
        y = temp; // 把 temp 赋值给 y
    }
    
    static void Main(string[] args)
    {
        NumberManipulator n = new NumberManipulator();
        int a = 100;
        int b = 200;
        Console.WriteLine("在交换之前,a 的值: {0}", a);
        Console.WriteLine("在交换之前,b 的值: {0}", b);
        n.swap(a, b); // 调用函数来交换值
        Console.WriteLine("在交换之后,a 的值: {0}", a);
        Console.WriteLine("在交换之后,b 的值: {0}", b);
        Console.ReadLine();
    }
}

/* 结果表明,即使在函数内改变了值,值也没有发生任何的变化 */
  • 按引用传递参数

引用参数是一个对变量的内存位置的引用,它不会创建新的存储位置,而是与实际参数具有相同的内存位置,使用 ref 关键字声明引用参数

class NumberManipulator
{
    public void swap(ref int x, ref int y)
    {
        int temp;
        temp = x; // 保存 x 的值
        x = y; // 把 y 赋值给 x
        y = temp; // 把 temp 赋值给 y
    }
    
    static void Main(string[] args)
    {
        NumberManipulator n = new NumberManipulator();
        /* ref参数需在传递前初始化变量 */
        int a = 100;
        int b = 200;
        Console.WriteLine("在交换之前,a 的值: {0}", a);
        Console.WriteLine("在交换之前,b 的值: {0}", b);
        n.swap(ref a, ref b); // 调用函数来交换值
        Console.WriteLine("在交换之后,a 的值: {0}", a);
        Console.WriteLine("在交换之后,b 的值: {0}", b);
        Console.ReadLine();
    }
}

/* 结果表明,swap 函数内的值改变了,且这个改变可以在 Main 函数中反映出来 */
  • 按输出传递参数

输出参数也是一个对变量的内存位置的引用,它会把方法输出的数据赋给自己,可从函数中返回两个值,使用 out 关键字声明输出参数;提供给输出参数的变量不需要赋值,而当需要从一个未指定参数初始值的方法中返回值时,输出参数特别有用

class NumberManipulator
{
    public void getValues(out int x, out int y)
    {
        /* out参数需在函数内部初始化变量 */
        Console.WriteLine("请输入第一个值:");
        x = Convert.ToInt32(Console.ReadLine());
        Console.WriteLine("请输入第二个值:");
        y = Convert.ToInt32(Console.ReadLine());
    }
   
    static void Main(string[] args)
    {
        NumberManipulator n = new NumberManipulator();
        int a, b;
        n.getValues(out a, out b); // 调用函数来获取值
        Console.WriteLine("在方法调用之后,a 的值: {0}", a);
        Console.WriteLine("在方法调用之后,b 的值: {0}", b);
        Console.ReadLine();
    }
}

/* 获取的结果完全取决于用户输入 */

十一、可空类型(Nullable)

1、Nullable 类型定义?

表示其基础值类型正常范围内的值,再加上一个 null 值,用于对 int, double, bool 等数据类型进行 null 的赋值

/* 声明一个 nullable 类型的语法 */
<data_type>? <variable_name> = null;

class NullablesAtShow
{
    static void Main(string[] args)
    {
        int? num1 = null; // 输出空
        int? num2 = 45;
        double? num3 = new double?(); // 输出空
        double? num4 = 3.14157;
        bool? boolval = new bool?(); // 输出空
        Console.WriteLine("显示可空类型的值: {0}, {1}, {2}, {3}", num1, num2, num3, num4);
        Console.WriteLine("一个可空的布尔值: {0}", boolval);
        Console.ReadLine();
    }
}

2、Null 合并运算符??

用于定义可空类型和引用类型的默认值,为类型转换定义了一个预设值,以防可空类型的值为 Null;Null 合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型,如果第一个操作数的值为 null,则返回第二个操作数的值,否则返回第一个操作数的值

class NullablesAtShow
{
    static void Main(string[] args)
    {
        double? num1 = null;
        double? num2 = 3.14157;
        double num3;
        num3 = num1 ?? 5.34; // 若 num1 为空则返回 5.34,否则返回 num1 的值
        Console.WriteLine("num3 的值: {0}", num3);
        num3 = num2 ?? 5.34; // 若 num2 为空则返回 5.34,否则返回 num2 的值
        Console.WriteLine("num3 的值: {0}", num3);
        Console.ReadLine();
    }
}

十二、数组(Array)

1、数组基础

/* 声明一个数组的语法 */
datatype[] arrayName;

// datatype:指定被存储在数组中的元素的类型
// []:指定数组的秩(维度),秩指定数组的大小
// arrayName:指定数组的名称

double[] balance; // 声明数组
double[] balance = {2340.0, 4523.69, 3421.0}; // 声明并赋值给数组

double[] balance = new double[10]; // 初始化一个长度为10的数组(元素为默认值)
balance[0] = 4500.0; // 通过索引号赋值给单个元素

int[] marks = new int[5]  {99,  98, 92, 97, 95}; // 创建并初始化一个数组
int[] marks = new int[]  {99,  98, 92, 97, 95}; // 省略数组长度的写法
int[] score = marks; // 赋值一个数组变量到另一个数组变量中

2、数组细节

  • 多维数组

多维数组

string [,] names; // 声明一个 string 变量的二维数组
int [,,] m; // 声明一个 int 变量的三维数组

(未完待续)

更多推荐

数据解析之Xpath解析(超详细定位)

目录​编辑前言一.Xpath介绍1.基本介绍2.HTML树状结构图2.节点之间的关系Xpath中的绝对路径与相对路径绝对路径相对路径二.Xpath的语法介绍基本定位语法1.元素属性定位2.层级属性结合定位3.使用谓语定位4.使用逻辑运算符定位5.使用文本定位6.使用部分函数定位三.Xpath语法验证在开发者工具的Ele

用一个RecyclerView实现二级评论

先上个效果图(没有UI,将就看吧),写代码的整个过程花了4个小时左右,相比当初自己开发需求已经快了很多了哈。给产品估个两天时间,摸一天半的鱼不过分吧(手动斜眼)需求拆分这种大家常用的评论功能其实也就没啥好拆分的了,简单列一下:默认展示一级评论和二级评论中的热评,可以上拉加载更多。二级评论超过两条时,可以点击展开加载更多

golang:context

context作用goroutine的退出机制多个goroutine都是平行的被调度的,多个goroutine如何协调工作涉及通信、同步、通知和退出通信:goroutine之间的通信同步chan通道同步:不带缓冲的chan提供了一个天然的同步等待机制。通过WaitGroup也可以为多个goroutine提供同步等待机制

数据库索引

一.索引的引用索引类似于一本书的目录,可以根据页码更快的查找到内容。索引可以加快访问数据库的速度二.索引的分类1.按表的列属性分类(1).普通索引:最基本的索引,没有任何限制;(2).唯一索引:索引列的值必须唯一,允许有空值。如果是组合索引,则列值的组合必须唯一;(3).主键索引:是一种特殊的唯一索引,一个表只能有一个

Golang代码漏洞扫描工具介绍——govulncheck

GolangGolang作为一款近年来最火热的服务端语言之一,深受广大程序员的喜爱,笔者最近也在用,特别是高并发的场景下,golang易用性的优势十分明显,但笔者这次想要介绍的并不是golang本身,而且golang代码的漏洞扫描工具,毕竟作为服务端的程序,安全性一直是一个不同忽视的地方Go安全团队在2022.09.0

基于Qt实现的可视化大屏监控

基于Qt实现的可视化大屏监控先上图基于Qt实现的可视化大屏监控总有人质疑QWidget实现不了炫酷的界面,其实QWidget已经很强大了,虽然很多效果没有现成的框架,所以比不上html5或者安卓这种,但是也能实现很多不错的效果了,而且在嵌入式设备上一样能达到这种效果,这是其他很多框架所不能实现的。本次大屏监控主要使用以

安达发APS|生产计划部门如何提升产量?

在当下制造业中,生产计划的制定和执行对于提高产量、降低成本、保证交货期等方面具有重要意义。随着科技的发展,越来越多的企业开始使用APS生产排程软件来优化生产计划,提高生产效率。本文将从以下几个方面介绍如何利用APS生产排程软件提升产量。1.需求预测与订单管理首先,企业需要对市场需求进行准确预测,以便合理安排生产计划。A

三、数学建模之非线性规划

1、定义2、例题matlan代码求解一、定义1.非线性规划(NonlinearProgramming,简称NLP)是一种数学优化问题的方法,它处理的目标函数或约束条件包含非线性项。与线性规划不同,非线性规划涉及到在非线性约束下寻找最优解。在许多领域都有广泛的应用,包括工程、经济学、物流、金融等。它可以用来解决各种实际问

Vue-01:MVVM数据双向绑定与Vue的生命周期

一、Vue介绍1.1什么是Vue?Vue是一个渐进式的JavaScript框架,用于构建用户界面。"渐进式"意味着Vue的设计理念是逐步增强应用的功能和复杂性,而不是一次性地引入所有功能。这使得开发者可以根据项目需求选择性地使用Vue的不同特性和功能。1.2Vue的优点Vue具有许多实际应用的优点,以下是其中一些:易学

全面了解SpringBoot拦截器

在本文中,我们将详细介绍SpringBoot中的拦截器,包括拦截器的概念、作用、实现方式、执行顺序、生命周期以及高级应用。最后,我们还将探讨拦截器的性能优化策略和常见问题。1.拦截器的概念和作用1.1什么是拦截器拦截器(Interceptor)是一种特殊的组件,它可以在请求处理的过程中对请求和响应进行拦截和处理。拦截器

前端代码规范

HTML编码规约(WC-HTML)-HTML编码规约前言本规约涉及HTML语言的编码风格、最佳实践。参与和反馈对规约有任何意见和建议,欢迎留言讨论:)1【推荐】使用2个空格缩进。统一使用2个空格缩进,不要使用4个空格或tab缩进:<!DOCTYPEhtml><html><head><title>Pagetitle</t

热文推荐