云计算百科
云计算领域专业知识百科平台

穿越?C# 修炼世界第六天:筑基方法(理论 01 + 实操 02)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

C# 修炼日记 · 2026 年 10 月 22 日

前五日,从练气入门一路打通数组与面向对象,属性、构造函数相继开光。今日峰主终于把目光落在修炼世界的"招法"上——方法(函数)。方法是封装好的一段功法:你刷一下身份证,闸机便自动判断买没买票;你把一个乱糟糟的数组丢进去,排序、转字符串一气呵成。峰主今日还传授了写方法的独门心法"反推法",以及不建对象也能直接调用的 static 静态功法。本卷理论篇(01)练"心明",把方法定义、四大分类、调用规则、重载、反推法与静态逐一吃透;实操篇(02)练"手熟",六道作业题全是方法的实战演练,其中重载、加权校验更是今日的重中之重。

第一部分 理论 01

今日功法速览

功法 / 法器用途说明
实例变量 / 局部变量 定义在类中 vs 定义在方法中:一个有默认值,一个必须手动初始化
方法(函数) 封装一段完整功能,写在类的大括号内,写给别人用
返回类型 void 无返回 / 具体数据类型有返回,二者写法不同
形参 / 实参 定义时的参数声明 vs 调用时传入的具体值,类型要匹配
return 有返回值的方法必写;无返回值也可写,作用是直接结束方法
方法四大分类 无参无返回值 / 有参无返回值 / 无参有返回值 / 有参有返回值
方法重载 方法名相同 + 参数列表不同,调用时按实参自动匹配
冒泡排序 Sort 自定义升序排序,整型 / 浮点型各一版(重载)
ToString 自定义数组转字符串输出,不再手动 for 遍历
反推法 先写调用逻辑,再逐步补方法实现——写方法的高级心法
身份证校验算法 前 17 位 × 权重求和 → 取模 11 → 映射出校验字符
静态 static 类名直接点调用,无需创建对象

第一章 昨日复盘:属性封笔,方法登台

开讲之前,峰主先把昨日属性收了个尾。昨日讲了类与对象,属性着墨颇多,方法几乎没讲——今日重点就是方法。

属性昨日已经悟透几件事:

  • 一个读写属性如果不带任何条件,它底层自动生成对应的私有字段,只是那行 private int _id; 不显示出来而已;
  • 属性只有两类:可读可写(有 get 有 set)和只读(只有 get),不存在只写;
  • 后续实际开发里,带条件的属性写得极少,一行 public int Id { get; set; } 就完事,不需要自己定义私有变量,直接公开属性。

峰主叮嘱:属性的门道昨日已尽,今日的主角是方法,全体竖起耳朵。

第二章 变量住哪,命就不同:实例变量 vs 局部变量

昨日有一个没展开的细节:变量写的位置不同,效果完全不同。峰主新建 OPDAY0201 项目,加了一个 stu 类,里面写一个 public int A;;又在 Test() 方法里写一个 int a;。两者一对比,高下立判:

class Stu
{
public int A; // 写在类里:实例变量(又叫成员变量、全局变量、属性)

public void Test()
{
// int a; // 写在方法里:局部变量
Console.WriteLine(A); // OK:对象创建后 A 已自动初始化,默认值 0
// Console.WriteLine(a); // 报错:局部变量不初始化,不能直接使用
}
}

对比项实例变量(类中)局部变量(方法中)
定义位置 类的大括号内 方法的大括号内
有无初始值 有(创建对象时自动初始化) 无(必须手动赋值才能用)
使用范围 当前类的所有普通方法都能用 只能在定义它的方法内用

关键在"初始化"三个字。这个 A 写在类里时并没有值,它要等到"出生"那一刻才有——也就是 new Stu() 创建对象的那一刻,new 关键字顺手把类里的所有实例变量初始化了一遍。所以不创建对象,A 根本不存在;创建了对象,A 就有默认值 0。回想数组 int[] arr = new int[10],十个元素个个有默认值——new 关键字的作用就是初始化,万物同源。

而方法里的局部变量没人给它初始化,你不赋值就用,编译器直接红叉。

峰主叮嘱:这个变量叫法很多,属性、成员变量、实例变量、全局变量都是它,说哪个都不算错,但心里要清楚——它们指的是同一个东西:定义在类里的变量。日后学静态变量,三者还要拿来做一次大对比,伏笔先埋下。

峰主伏笔:从今日起写作业,一个项目就能写完所有题,不必一题一个项目——这正是有了类的好处。

第三章 方法是什么:一段被封装的功法

方法的定义就一句话:封装了一段完整功能。它是写在类的大括号里面的,不能脱离类单独存在。

峰主连打三个比方,务必吃透:

  • 蛋炒饭:煎蛋、放配菜、翻炒,一整套流程下来才有一碗蛋炒饭——这就是"炒饭"这个功能的封装;
  • 高铁闸机:你进站只做了"刷一下身份证"这一个动作,闸机内部却完成了一整套校验,最终返回"可以进 / 没购票"——你只是把身份证传进去了,换来一个返回结果;
  • 公司派活:周一开会分任务,周五项目经理直接跑你的方法验收,方法调用出错责任就是你的——方法就是你的工作任务。
  • 既然方法是写给别人用的,修饰词就必须是 public(公开)。写个 private 方法别人根本调不到,那写了还有什么意义?

    峰主叮嘱:以后去公司,你的活就是写方法——把一段任务封装好,别人拿来就用。

    第四章 方法定义五要素

    一个方法由五部分组成,缺一不可:

    public int Add(int a, int b)
    {
    return a + b;
    }

    要素说明
    修饰词 默认 public,公开给别人调用
    返回类型 无返回写 void;有返回写返回的数据类型(int、double、string……)
    方法名 见名知义 + 首字母大写
    形参列表 小括号里的变量声明,可以有 0 个或多个,多个用逗号隔开
    方法体 大括号里的具体实现;有返回值必须写 return,且返回内容与声明类型匹配
    • 返回类型:峰主用"让妈妈做蛋炒饭"类比——无返回相当于妈妈让你自己滚下去炒(void,默默做事不交结果);有返回相当于妈妈把炒好的蛋炒饭端给你(返回一个整数、小数、字符串……)。返回类型写成什么,取决于方法最终返回什么。
    • 方法名:见名知义、首字母大写。峰主点名批评:昨天不少人方法名没大写,从今天起所有方法名必须首字母大写,养成习惯,否则日后是致命的打击。
    • 形参:就是变量的声明,只是"形式上的参数",相当于留一个口子接收外面传进来的东西,别人传什么你就答什么。
    • return:有返回值的方法必写;return 的返回内容要和声明时的数据类型匹配。

    峰主叮嘱:这五要素就是方法的"开炉之法",往后写方法全都照着这套来。

    第五章 方法四大分类与调用规则

    方法按"有没有参数、有没有返回值"分成四大类:

    分类写法特征使用频率
    无参无返回值 public void Method01() 低
    有参无返回值 public void Method02(int a) 高
    无参有返回值 public double Method03() 低
    有参有返回值 public int Method04(int a, int b) 高

    课堂逐一演示(以下代码均可编译运行):

    class Stu
    {
    // 无参无返回值
    public void Method01()
    {
    Console.WriteLine("你好");
    }

    // 有参无返回值:形参 a 直接用,无需额外赋值
    public void Method02(int a)
    {
    Console.WriteLine(a);
    }

    // 无参有返回值:必须有 return,返回 5.6
    public double Method03()
    {
    return 5.6;
    }

    // 有参有返回值:return a + b 的和
    public int Method04(int a, int b)
    {
    return a + b;
    }
    }

    调用时三条铁律:

  • 有参方法必须传参:传的是实参(具体值),类型必须与形参一致,或比它小(byte、short 等可以隐式转换的也可以)。传个 7.8 给 int a,直接报错。形参是定义时的占位,实参是调用时的真值——写的时候是形参,调用的时候是实参。
  • 有返回值必须"先接收,再输出":double d = s.Method03(); Console.WriteLine(d); 接收后再处理;也可以直接把方法调用塞进输出语句(Console.WriteLine(s.Method03());)效果一样。无返回值的方法直接调用即可。看 Random 就懂:int a1 = r.Next(); 先接收随机数,再输出——因为 Next() 是有返回值的方法。
  • 当前类内部调用自身方法,不需要 new 对象:在 Stu 类内部直接 Method02(7); 就能调,外面才需要 Stu s = new Stu(); s.Method02(7);。
  • IDE 里怎么认这些"招法"图标:方法是五边形里一个 Y,属性是扳手,字段是长方形——一看便知。方法调用后面永远带小括号 (),属性调用不带。

    运行效果:

    Method02(7) -> 7
    Method03() -> 5.6
    Method04(5,6) -> 11

    峰主叮嘱:还有个容易走火的地方——return 和 break 不是一回事。break 只是退出循环,return 是直接结束整个方法,return 后面的代码永远执行不到(会变灰)。有返回值的方法写 return 值;,无返回值的方法也可以写一个光秃秃的 return; 提前收工。

    第六章 方法重载:同名不同招

    场景来了:Method04 只能求两个整数的和。想求两个小数怎么办?再写一个 double 版本?方法名能不能还用 Method04?

    能。方法名一致、参数列表不同,就叫方法的重载。

    峰主新建数学类 Mash,演示了一整套重载:

    class Mash
    {
    // 求两个整数的和
    public int Sum(int a, int b)
    {
    return a + b;
    }

    // 重载:求两个小数的和
    public double Sum(double a, double b)
    {
    return a + b;
    }

    // 重载:求数组的和
    public int Sum(int[] a)
    {
    int sum = 0;
    for (int i = 0; i < a.Length; i++)
    {
    sum += a[i];
    }
    return sum;
    }
    }

    调用时编译器按实参自动挑版本:传 3, 4 走整数版;传 3.0, 4.5 走小数版;传数组走数组版。参数列表相同就会报错——编译器不知道你传两个整数时该调哪一个;只要参数列表不同(数量不同、类型不同都算),随便写几个都不冲突。

    Mash m = new Mash();
    Console.WriteLine(m.Sum(3, 4)); // 7
    Console.WriteLine(m.Sum(3.0, 4.5)); // 7.5
    Console.WriteLine(m.Sum(new int[]{1,2,3,4,5})); // 15

    为什么要有重载?因为功能相同、实现不同的事,就该共用一个名字——都在求和,凭什么一个叫 Sum 一个叫 Add?系统的 Console.WriteLine 就是重载大户:输出整数、小数、字符串、布尔都有对应版本,唯独没有无参版本,所以 Console.WriteLine(); 直接报错。Random.Next() 也是重载:传一个参数取 0 到 n,传两个参数取 n 到 m。

    峰主伏笔:重载不只用在方法上,构造函数也能重载。方法重载 + 冒泡排序 + 转字符串的组合拳马上就来——这正是今天课堂布置的实操任务,也是你作业题 5 的同一招。

    第七章 反推法:身份证校验实战

    7.1 先讲"方法没实现"的教训

    峰主先演示了一个反例:项目经理丢给你一个排序方法,你"已有一个方法"却压根没实现(方法体是空的),调用后数组输出顺序纹丝不动——排序根本没起作用。写方法,光有壳子不行,必须把实现填上。整型数组升序用冒泡:

    public void Sort(int[] arr)
    {
    for (int i = 0; i < arr.Length – 1; i++)
    {
    for (int j = 0; j < arr.Length – 1 – i; j++)
    {
    if (arr[j] > arr[j + 1])
    {
    int t = arr[j];
    arr[j] = arr[j + 1];
    arr[j + 1] = t;
    }
    }
    }
    }

    写完 int 版,复制一份把 int 改成 double,浮点数组也能排——这就是重载的实战。

    再看"数组转字符串":直接输出数组只会得到一串地址,要的是 4,34,224,422,542,4244 这样的字符串。那就遍历 + 拼接:

    public string ToString(int[] arr)
    {
    string s = "";
    for (int i = 0; i < arr.Length; i++)
    {
    s += arr[i];
    if (i < arr.Length – 1)
    {
    s += ",";
    }
    }
    return s;
    }

    有了它,Main 里一句 Console.WriteLine(arrTool.ToString(arr)); 就完事,再也不用手动 for 循环输出了——这就是方法的价值:把常用的活儿封装起来,拿来就用。同样的,复制一份把参数换成 double[] 就是浮点版重载。

    运行效果(以课堂示例数组 {8,3,4,5,4,2,2,2,4,4} 演示):

    排序后输出:2,2,2,3,4,4,4,4,5,8

    7.2 经典案例:验证身份证合法性

    峰主抛出今日最经典的案例:身份证合法性校验。身份证 18 位由四段组成——前 6 位地址码(行政区划)、8 位出生日期、3 位顺序码(奇数男、偶数女)、最后 1 位校验码。而最后一位不是拍脑袋定的,是由前 17 位算出来的:

    • 前 17 位分别乘以固定权重:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2;
    • 加权求和,再对 11 取模(sum % 11);
    • 余数 0~10 依次映射校验字符:1 0 X 9 8 7 6 5 4 3 2。

    7.3 反推法:先写调用方,再补方法

    峰主强调:写方法最高级的心法是反推法——不知道方法怎么写没关系,先知道要实现什么,把"调用逻辑"写好,报错的地方再一个个补方法。

    像高铁闸机一样,程序只需"刷身份证":输入前 17 位 → 算出校验字符 → 输入最后一位 → 比较。调用方先写成这样:

    Card card = new Card();
    Console.WriteLine("请输入身份证的前17位");
    string s = Console.ReadLine(); // 第 1 步:输入前 17 位
    char ch = card.YanZheng(s); // 第 2 步:算出校验字符(方法待补)
    Console.WriteLine("请输入身份证的最后一位");
    char ch1 = char.Parse(Console.ReadLine()); // 第 3 步:输入最后一位
    if (ch1 == ch)
    Console.WriteLine("身份证合法");
    else
    Console.WriteLine("身份证不合法");

    card.YanZheng(s) 报错?那就去补这个方法,一步步反推:

    class Card
    {
    // 反推第 1 层:前17位 × 权重求和 → 取模11 → 映射字符
    public char YanZheng(string str)
    {
    int[] arr = new int[17] { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
    long sum = Sum(str, arr); // 反推第 2 层:求和的方法
    long a = sum % 11;
    char c = GetChar(a); // 反推第 3 层:余数转字符的方法
    return c;
    }

    // 字符串的每一位数字 × 权重,累加
    public long Sum(string str, int[] arr)
    {
    long[] arr1 = Parse(str); // 反推第 4 层:字符串转数字数组
    long sum = 0;
    for (int i = 0; i < arr1.Length; i++)
    {
    sum += arr1[i] * arr[i];
    }
    return sum;
    }

    // 17 位字符串 → 数字数组(17 位已超过 int 上限,用 long)
    public long[] Parse(string s)
    {
    long[] arr = new long[17];
    long l = long.Parse(s);
    int index = 16;
    while (l > 0)
    {
    arr[index] = l % 10; // 取个位
    l /= 10; // 去掉个位
    index—; // 下标往前挪
    }
    return arr;
    }

    // 余数 → 校验字符
    public char GetChar(long a)
    {
    char[] c = new char[11] { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' };
    return c[(int)a];
    }
    }

    为什么要用 long? 17 位数字早就爆了 int 的上限,只能用 long。课堂上峰主就栽在这里:一开始用 int 强转,打桩测试(临时输出中间变量排查,就叫打桩测试)发现第 4 位之后数据全错——long 强转成 int 出了乱子,最后统一改成 long 类型,一次通过。写代码要养成"先想类型会不会溢出"的习惯。

    运行效果(示例前 17 位 11010519491231002,加权和 167,167 % 11 = 2,映射校验字符 ‘X’):

    请输入身份证的前17位
    11010519491231002
    请输入身份证的最后一位
    X
    身份证合法

    峰主叮嘱:反推法的精髓是"每一层都看得清清楚楚"——验证合法性 → 求校验字符 → 加权求和 → 转数组,每一环封装成一个方法,读代码像读账本。你的作业题 6(EAN-13 条码校验)就是这套加权求和的亲兄弟。

    第八章 静态:不建对象也能用的功法

    前六天学的变量、方法,都要"先 new 对象、再点着用"。今天峰主再传一招——static(静态)。

    • 实例变量 / 实例方法:不加 static,必须 new 出对象,对象名.变量/方法;
    • 静态变量 / 静态方法:加 static,也叫类变量 / 类方法,不用创建对象,直接 类名.变量/方法。静态方法里只能用静态变量,不能用未实例化的实例变量。

    回头看,其实从第一天起你就在用静态方法:

    用法真相
    Console.WriteLine(…) Console 是类名,WriteLine 是静态方法
    Array.Sort(arr)、Array.Resize(…) Array 类是静态方法大户,排序调大小现成的
    Math.Abs(…)、Math.Pow(…) Math 类的静态方法,取绝对值、求幂拿来就用
    Random r = new Random(); r.Next(); 唯一要先 new 的——它是实例方法

    这就是为什么学了 static 要看懂别人的代码:官方早把 Array、Math、Console 封装成静态功法,直接 类名.点 就完事;没有现成方法时,才自己手写。

    峰主还演示了自定义封装:原生数组没有直接求最大值的方法,那就建一个 ArrayDemo 类自己封装:

    class ArrayDemo
    {
    public int GetMax(int[] a)
    {
    ArrayTool.Sort(a); // 先升序排序
    return a[a.Length – 1]; // 最后一个元素就是最大值
    }

    public int GetMaxIndex(int[] a)
    {
    if (a.Length == 0)
    {
    return –1; // 空数组兜底,避免无返回值
    }
    int max = GetMax(a);
    for (int i = 0; i < a.Length; i++)
    {
    if (a[i] == max)
    {
    return i; // 多个最大值时返回第一个
    }
    }
    return –1;
    }
    }

    (示例数组 {3, 8, 5, 8, 1} 实测:GetMax → 8;但因为 GetMax 内部先排了序,数组已变成 {1, 3, 5, 8, 8},所以 GetMaxIndex → 3——排序会改动原数组,这副作用日后要留心。)

    峰主叮嘱:本次作业不需要用 static,方法统一按实例方法写(修饰词 public 即可);讲 static 只是为了让你看懂 Console.WriteLine、Array.Sort 为什么能直接"类名点"调用,免得日后疑惑。

    峰主伏笔:方法、属性、构造函数——类里这三样正一一揭开,明日继续。

    走火入魔预警

    反噬类型错误术式正确术式
    局部变量没初始化就用 方法里 Console.WriteLine(a); 编译报错 先 int a = 0; 赋值再用;类中变量靠 new 初始化
    有返回值方法忘写 return public int Sum(…) 缺 return 报错 有返回值必写 return 值;,类型与声明一致
    方法名不按规范 public void show() 小写开头的旧习 首字母大写 + 见名知义:Show()
    形参实参类型不匹配 给 int a 传 7.8 报错 类型一致,或传可隐式转换的 byte、short
    return 与 break 混为一谈 想在方法中间用 break 提前收工 return 结束整个方法;break 只退出循环
    重载参数列表相同 写两个 Sum(int, int) 报错 同名 + 参数列表不同(数量/类型不同)
    身份证 17 位用 int 存 int.Parse 溢出报错 用 long.Parse
    long 强转 int 数据错乱 (int)(l % 10) 中间数据全错 直接统一用 long 类型,避免强转
    方法只有壳没有实现 空方法调用后"没反应" 先填方法体再调用(冒泡/拼接写完整)
    排序副作用改原数组 GetMax 排序后 GetMaxIndex 结果变了 记住 Array.Sort 会改动原数组,留意调用顺序

    今日修炼总结

    今日心法,可归纳为八层:

  • 方法:封装一段完整功能的功法,写在类里,公开给别人用——以后上班就是写方法;
  • 变量出身:类中实例变量(成员/全局/属性)随 new 自动初始化有默认值;方法内局部变量必须手动赋值,作用域只在方法内;
  • 五要素:修饰词 public → 返回类型(void/数据类型)→ 方法名(大写+见名知义)→ 形参列表 → 方法体 + return;
  • 四大分类:无参无返回值 / 有参无返回值 / 无参有返回值 / 有参有返回值,后两类用得最多;
  • 调用铁律:有参必传实参且类型匹配;有返回值先接收再输出;类内调用不 new;return 结束方法、break 退出循环;
  • 重载:同名不同参,按实参自动匹配,参数列表相同必报错——Console.WriteLine、Random.Next 皆是重载;
  • 反推法:先写调用方,报错处层层补方法;身份证校验 = 权重求和 + 取模 + 映射,long 防溢出;
  • 静态:static 方法类名直接点、无需 new;Console、Array、Math 都是官方静态功法,本次作业不用 static。
  • 峰主有言:方法的修炼重在一个"封"字——把功能封起来,把调用露出去。今日八层心法若只记住一句,就记"有返回值先接收、再输出;重载同名不同参"。


    第二部分 实操 02

    理论既明,今日正式上手方法实战。峰主说今天的作业要自己动手把方法一个个"搓"出来:判断闰年、登录校验、交换变量、数组求和、重载取最值、条码校验——六道题几乎覆盖了方法的所有用法。以下为今日修炼实录,全部功法均已在 .NET 控制台应用中编译运行通过,运行效果均为真实跑出的输出。

    今日功法速览

    功法 / 法器用途说明
    方法封装 把判断、计算、交换逻辑写进方法,Main 里只负责调用
    属性 set 判断 登录校验写在 set 里,输入即拦截
    重载 ZSum int / double / char 三版同名方法取最大值
    加权求和 EAN-13 校验位算法,与理论篇身份证校验同源
    值传递 方法内交换变量不影响实参(题 3 的坑)

    基础题一:平年还是闰年

    【题目要求】 写一个方法,判断是平年还是闰年:能被 4 整除不能被 100 整除,或者能被 400 整除。

    【修炼思路】 建一个 Year 类,写一个 Pdyear(int year) 方法,把闰年的两个条件用 &&、|| 串起来,满足条件返回"闰年",否则返回"平年"。这是典型的有参有返回值方法——传一个年份进去,返回一个判断结果。

    【代码】

    //类
    class Year
    {
    //属性
    public string Pdyear(int year)
    {
    //判断
    if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
    {
    return "闰年";
    }
    return "平年";
    }
    }

    //实例化
    Year a = new Year();
    //调用Pdyear方法
    string year = a.Pdyear(2026);
    Console.WriteLine(year);

    【运行效果】

    平年

    (再验一发 2024:Pdyear(2024) 输出 闰年——2024 能被 4 整除且不能被 100 整除。)

    【修炼要点】 % 取余判断整除:year % 4 == 0 表示能被 4 整除;&& 优先级高于 ||,整条判断写成 year % 4 == 0 && year % 100 != 0 || year % 400 == 0 就是"(能被4整除 且 不能被100整除)或 能被400整除"。方法名 Pdyear(判断年)+ 首字母大写,符合见名知义规范。

    基础题二:登录校验

    【题目要求】 分别提示用户输入用户名和密码,写一个方法判断用户输入是否正确,返回给用户一个登录结果,并且还要单独返回登录信息:用户名错误或密码错误时,除了返回登录结果,还要返回"用户错误"或"密码错误"。

    【修炼思路】 建一个 User 类,把判断写进属性的 set 里——用户名给 Name 属性,密码给 Password 属性,赋值的一瞬间就完成校验输出。正确账号:用户名 root,密码 1@3$5^7*8(0。这就是理论篇"属性 set 里可以设条件"的实战应用。

    【代码】

    //定义用户类
    class User
    {
    //定义账户和密码字段
    private string _name;
    private string _password;
    //判断用户名输入
    public string Name
    {
    get
    {
    return _name;
    }
    set
    {
    if (value == "root")
    {
    _name = "root";
    }
    else
    {
    Console.WriteLine("登录失败");
    //返回模糊错误
    Console.WriteLine("用户名或者密码错误");
    }
    }
    }
    //判断密码输入
    public string Password
    {
    get
    {
    return _password;
    }
    set
    {
    if (value == "1@3$5^7*8(0")
    {
    _password = "1@3$5^7*8(0";
    Console.WriteLine("登录成功");
    Console.WriteLine($"{_name},欢迎你");
    }
    else
    {
    Console.WriteLine("登录失败");
    Console.WriteLine("用户名或者密码错误");
    }
    }
    }
    }

    //调用
    User b = new User();
    Console.Write("请输入用户名:");
    b.Name = Console.ReadLine();
    Console.Write("请输入密码:");
    b.Password = Console.ReadLine();

    【运行效果】

    正确登录(用户名 root、密码 1@3$5^7*8(0):

    请输入用户名:root
    请输入密码:1@3$5^7*8(0
    登录成功
    root,欢迎你

    密码错误(用户名 root、密码 123456):

    请输入用户名:root
    请输入密码:123456
    登录失败
    用户名或者密码错误

    【修炼要点】 set 里 value 就是赋值传进来的值,if (value == "root") 就是"输入的是不是 root"。这里有个隐藏隐患:用户名错误时 _name 没被赋值,若此时密码反而输对了,程序会先报"登录失败",随后又输出"登录成功"和一句没有名字的欢迎语(实测输出 登录成功、,欢迎你)。真正严谨的写法应该让"用户名错误"直接拦住后续校验——峰主说过,属性 set 里可以加条件,也可以直接"拦截",这正是封装的本意。

    基础题三:交换两个 int 变量

    【题目要求】 使用方法交换两个 int 类型的变量。

    【修炼思路】 建一个 IntExchange 类,写 IE(int a, int b) 方法,用经典三行交换法(temp 暂存 → a 换 b → b 换 temp),交换后把结果打印出来。注意观察运行效果里的玄机——见修炼要点。

    【代码】

    //定义类
    class IntExchange
    {
    //方法
    public int IE(int a, int b)
    {
    int temp = a;
    a = b;
    b = temp;
    Console.WriteLine($"{a}\\n{b}");
    return –1;
    }
    }

    //实例化
    IntExchange a = new IntExchange();
    int one = 23;
    int two = 30;
    a.IE(one, two);

    【运行效果】

    30
    23

    【修炼要点】 方法内部确实把 a、b 交换了(输出 30 和 23),但注意——实参 one、two 本身没有被改变,调用结束后 one 仍是 23、two 仍是 30。这就是 C# 的值传递:方法拿到的是值的"复印件",改复印件不影响原件。想真正交换外面的变量,得靠引用类型参数或后续学的 ref/数组,这是本题最大的坑,也是日后最容易踩的雷。

    基础题四:任意长度数组求和

    【题目要求】 求任意长度数组的和(整型数组)。

    【修炼思路】 建一个 Arrsum 类,写 Asum(int[] a) 方法,用 for 循环遍历数组,把每个元素累加到 sum,最后 return。参数直接传数组——这是方法形参的新玩法:形参可以是一个数组变量。

    【代码】

    //定义类
    class Arrsum
    {
    public int Asum(int[] a)
    {
    int sum = 0;
    //遍历累加
    for (int i = 0; i < a.Length; i++)
    {
    sum += a[i];
    }
    return sum;
    }
    }

    //实例化
    Arrsum a = new Arrsum();
    int[] arr = new int[10] { 3, 4, 2, 5, 6, 7, 1, 7, 6, 7 };
    Console.WriteLine(a.Asum(arr));

    【运行效果】

    48

    (3+4+2+5+6+7+1+7+6+7 = 48,实跑验证无误。)

    【修炼要点】 int[] a 作为形参,实参传一个数组进来,方法里 a.Length 拿到数组长度,a[i] 逐个取元素。不管数组多长都能求——这就是"任意长度"的含义,也是"方法封装"带来的复用价值:以后所有数组求和,一行 a.Asum(arr) 搞定。

    基础题五:重载求最大值

    【题目要求】 创建方法求两个数的最大值,两个数可以是整数、小数或字符,要求使用重载。

    【修炼思路】 建一个 Sum 类,写三个同名 ZSum 方法:整型版返回 int、小数版返回 double、字符版返回 char,参数列表各不相同——这就是理论篇的方法重载。调用时传整数走整型版、传小数走小数版、传字符走字符版,编译器自动匹配。

    【代码】

    class Sum
    {
    //方法 整型
    public int ZSum(int a, int b)
    {
    if (a >= b)
    {
    return a;
    }
    else
    {
    return b;
    }
    }
    //方法重载,小数
    public double ZSum(double a, double b)
    {
    if (a >= b)
    {
    return a;
    }
    else
    {
    return b;
    }
    }
    //方法重载,字符
    public char ZSum(char a, char b)
    {
    if (a >= b)
    {
    return a;
    }
    else
    {
    return b;
    }
    }
    }

    Sum a = new Sum();
    Console.WriteLine(a.ZSum(35, 67));
    Console.WriteLine(a.ZSum(3.5, 6.7));
    Console.WriteLine(a.ZSum('a', 'A'));

    【运行效果】

    67
    6.7
    a

    【修炼要点】 三个方法名都叫 ZSum,参数列表分别是 (int,int)、(double,double)、(char,char)——方法名一致 + 参数列表不同 = 重载。ZSum(35, 67) 走整型版返回 67;ZSum(3.5, 6.7) 走小数版返回 6.7;ZSum('a', 'A') 走字符版返回 a——字符比较的是 Unicode 码值,小写 a(97)比大写 A(65)大。字符类型用单引号,和字符串的双引号要分清。

    基础题六:EAN-13 / UPC-A 零售商品条码校验

    【题目要求】 13 位(或 12 位)数字,最后一位是校验位。以 EAN-13 为例:从右往左(不含校验位)交替乘以权重 1、3,加权求和后取 S mod 10,(10 – S mod 10) mod 10 得到校验位;总和加上校验位后必须是 10 的倍数才合法。

    【修炼思路】 建一个 Ean 类,写 Jqqh(int[] a)(加权求和)方法:遍历数组前 12 位,偶数位(按本实现口径,即从左数第 2、4、6……位)乘以 3,累加后再加上校验位本身,返回总和;Main 里判断总和能否被 10 整除,输出"合法 / 不合法"。这和理论篇身份证校验的"加权求和"是同门功法。

    【代码】

    class Ean
    {
    //加权求和
    public int Jqqh(int[] a)
    {
    int sum = 0;
    for (int i = 0; i < a.Length – 1; i++)
    {
    //判断偶数位
    if ((i + 1) % 2 == 0)
    {
    a[i] *= 3;
    }
    sum += a[i];
    }
    return sum + a[a.Length – 1];
    }
    }

    Ean a = new Ean();
    int[] arr = new int[13] { 6, 9, 2, 5, 3, 0, 3, 7, 0, 1, 7, 5, 8 };
    int number = a.Jqqh(arr);
    if (number % 10 == 0)
    {
    Console.WriteLine("合法");
    }
    else
    {
    Console.WriteLine("不合法");
    }

    【运行效果】

    加权求和 = 110
    合法

    (实测:6 + 9×3 + 2 + 5×3 + 3 + 0×3 + 3 + 7×3 + 0 + 1×3 + 7 + 5×3 + 8 = 110,110 % 10 == 0 → 合法。)

    【修炼要点】 这是"加权求和 + 取模判断"的典型应用,和身份证校验同宗同源。两个细节要留心:其一,本代码按"从左数偶数位 ×3"实现((i+1) % 2 == 0 时 a[i] *= 3),而题目说明的 EAN-13 标准口径是"从右往左数(校验位不算,第 2、4、6……位 ×3)"——两种口径对同一串数字结果可能不同,运行以本代码实际输出为准;其二,a[i] *= 3 会直接修改原数组,和理论篇排序的副作用一样,要心里有数。

    走火入魔预警

    反噬类型错误术式正确术式
    闰年条件写错优先级 `year % 4 == 0
    交换方法"看似生效" 以为 a.IE(one, two) 后 one、two 已互换 值传递只交换方法内副本,实参不变;要改外部变量需引用/数组/ref
    用户名错误还继续走密码 用户名错了 _name 没赋值,密码对时输出"登录成功,欢迎你"缺名字 用户名错误应直接拦截,不进入密码校验
    字符当字符串用 a.Gender = "男" 报错(本日 'a'、'A' 同理) char 用单引号,string 用双引号
    重载版次乱写 两个方法参数列表相同还叫重载 同名 + 参数列表不同(数量/类型不同)
    加权求和方向搞混 标准"从右往左"与实现"从左偶数位"混用 明确本代码口径,以运行输出为准
    加权时改了原数组 a[i] *= 3 后数组值被污染,后续复用出错 需要保留原值时另开数组存储

    今日修炼总结

    六道作业题走下来,方法的全套用法已过一遍:

    • 有参有返回值:题 1 判断闰年、题 4 数组求和、题 5 重载取最大值、题 6 条码校验——传参进去,返回结果;
    • 属性 set 做判断:题 2 登录校验,把拦截逻辑写进属性赋值的一瞬间;
    • 值传递的坑:题 3 交换方法内有效、实参不动——这是今日最值得记的教训;
    • 重载实战:题 5 同一方法名三版(int/double/char)自动匹配,正是理论篇重载的作业版;
    • 加权求和:题 6 与理论篇身份证校验同源,条码、身份证一法通吃。

    峰主说今天作业"不难但极考细心":重载参数列表、值传递、字符单引号、加权方向,哪一处走神都会编译报错或结果跑偏。功法皆已实测跑通——六题输出 平年 / 登录成功 / 30 23 / 48 / 67 6.7 a / 110 合法,全部真实可复现。为明日方法进阶(静态、属性、构造函数继续深化)打下地基。

    下一关,我们继续。

    (本文为个人修炼记录,功法均可在 .NET 控制台应用下编译运行通过。如有谬误欢迎指正,一同精进。)

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 穿越?C# 修炼世界第六天:筑基方法(理论 01 + 实操 02)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!