一、课前准备
1、参考资料
《C#图解教程》细而全
《传智播客面试宝典》
《C#语言规范》
2、如何学C#
杨中科是谁?
如何选择语言?
做的是程序员,不是C#程序员。选定C#后,不要纠结,要深入学习学懂,这样转
语言时非常容易。现在选定C#,就需要学深学懂。
为什么不会做练习?
如游泳,老师光教,不下水练习。再怎么教也不会游泳。
反复练习,温故而知新。做笔记复习很重要。每一次的复习才是收获。
3、课程中加*的有难度,并不要求全掌握,先把常规内容学会。
进阶系列难度所处的范围大概如下:
二、复习检测
1、声明两个变量:int n1= 10,n2= 20要求将两个变量交换,最后输出n1为 20,n2为 10。
扩展(*):不使用第三个变量如何交换?
int n1 = 10, n2 = 20;
n1 = n1 + n2;
n2 = n1 - n2;
n1 = n1 - n2;
Console.WriteLine($"{n1},{n2}");
技巧:
异或:有且只有一个为1时为1,否则为0.
例:1^0=1,1^1^=0,0^1=1,0^0=0, 可以看出异或对于数的顺序不敏感。
同时注意一下:1^0^0=1,1^1^1=1,1^0^1=0,1^1^0=0
可以去体会一下两点:
1)异若的顺序不影响结果值;
2)关键:一个数连续异或同一个数,等于原数。例如 3^4^4=3
注意顺序不影响:4^3^4=3
因此本题可用异或来解答:
int a = 10, b = 20;
a = a ^ b;//a=(a^b)
b = a ^ b;//相当于b=(a^b)^b=a^b^b=a,即此时b=a了
a = a ^ b;//相当于a=(a^b)^(a)=a^b^a=b
2、用方法来实现:将上题封装一个方法来做,方法有两个参数分别为num1,num2。将num1
与num2交换。提示:方法有两个参数n1,2在方法中将n1与n2进行交换,使用ref。(*)
private static void Main(string[] args)
{
int n1 = 10, n2 = 20;
Exchange(ref n1, ref n2);
Console.WriteLine($"{n1},{n2}");
Console.ReadKey();
}
private static void Exchange(ref int n1, ref int n2)
{
n1 = n1 + n2;
n2 = n1 - n2;
n1 = n1 - n2;
}
技巧:vs2022
快速生成函数/方法:在主函数中输完函数后如上例Exchange(ref n1, ref n2);这样转
时,按下Alt+Enter,弹出的小菜单中选择生成方法,即可快速生成。
3、请用户输入一个字符串,计算字符串中的字符个数,并输出。
private static void Main(string[] args)
{
Console.WriteLine("请输入一个字符串:");
string s = Console.ReadLine();
Console.WriteLine($"{s}共有{s.Length}个字符");
Console.ReadKey();
}
4、用方法来实现:计算两个数的最大值。思考:方法的参数? 返回值?
扩展(*): 计算任意多个数间的最大值(提示:使用可变参数,params)。
private static void Main(string[] args)
{
Console.WriteLine(GetMax(10, 20));
Console.WriteLine(GetMax(10, 20, 30));
Console.WriteLine(GetMax(10, 20, 30, 40));
Console.ReadKey();
}
private static int GetMax(params int[] n)
{
int max = n[0];
for (int i = 1; i < n.Length; i++)
{
if (n[i] > max)
{
max = n[i];
}
}
return max;
}
5、用方法来实现:计算 1-100 之间的所有整数的和。
6、用方法来实现:计算 1-100 之间的所有奇数的和。
7、用方法来实现:判断一个给定的整数是否为“质数”。
8、用方法来实现: 计算 1-100 之间的所有质数《素数)的和1060。
private static void Main(string[] args)
{
Console.WriteLine(GetSum(100));
Console.ReadKey();
}
private static int GetSum(int n)
{
int sum = 0;
for (int i = 1; i <= n; i++)
{
if (IsPrime(i))
{
sum += i;
}
}
return sum;
}
private static bool IsPrime(int n)
{
if (n == 1) return false;
else if (n == 2) return true;
else //n>2
{
for (int i = 2; i <= Math.Sqrt(n); i++)
{
if (n % i == 0)
{
return false;
}
}
return true;
}
}
技巧:
凡是找与数学有关的函数,用Math。
9、用方法来实现:有一个整数数组:{1,3,5,7,9,2,4,6,8,10},找出其中最大值,并输出.
不能调用数组的Max()方法。
private static void Main(string[] args)
{
int[] n = new int[] { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 };
Console.WriteLine(GetMax(n));
Console.ReadKey();
}
private static int GetMax(int[] n)
{
int max = n[0];
for (int i = 0; i < n.Length; i++)
{
if (max < n[i])
{
max = n[i];
}
}
return max;
}
10、用方法来实现:有一个字符数组{"马龙","迈克尔乔丹","雷吉米勒","蒂姆邓肯",
"科比布莱思特"},请输出最长的字符串
private static void Main(string[] args)
{
string[] s = new string[] { "马龙", "迈克尔乔丹", "雷吉米勒", "蒂姆邓肯", "科比布莱思特" };
Console.WriteLine(s[GetMax(s)]);
Console.ReadKey();
}
private static int GetMax(string[] n)//返回最长字串的索引
{
int max = 0;
for (int i = 0; i < n.Length; i++)
{
if (n[max].Length < n[i].Length)
{
max = i;
}
}
return max;
}
11、用方法实现:请计算出一个整形数组的平均值,{1,3,5,7,93,33,4,4,6,8,10}。要求:
计算结果如果有小数,则显示小数点后两位(四舍五入)
private static void Main(string[] args)
{
int[] n = { 1, 3, 5, 7, 93, 33, 4, 4, 6, 8, 10 };
Console.WriteLine(GetAvg(n));
Console.ReadKey();
}
private static double GetAvg(int[] n)
{
double sum = 0;
for (int i = 0; i < n.Length; i++)
{
sum += n[i];
}
return Math.Round(sum / n.Length, 2);
}
12、请通过冒泡排序法对整数数组{1,3,5,7,99,2,4,6,8,10}实现升序排序。
private static void Main(string[] args)
{
int[] n = { 1, 3, 5, 7, 99, 2, 4, 6, 8, 10 };
int temp;
for (int i = 0; i < n.Length - 1; i++)
{
for (int j = i + 1; j < n.Length; j++)
{
if (n[i] > n[j])
{
temp = n[i];
n[i] = n[j];
n[j] = temp;
}
}
}
for (int i = 0; i < n.Length; i++)
{
Console.Write(n[i] + " ");
}
Console.ReadKey();
}
13、有如下字符串:患者:“大夫,我咳嗽得很重。";大夫:"你多大年龄?";患者:"七十五
岁。";大夫:"二十岁咳嗽吗";患者:"不咳嗽。";大夫:"四十岁时咳嗽吗?";患者:"
也不咳嗽。";大夫:"那现在不咳嗽,还要等到什么时咳嗽?";。
需求:统计"咳嗽"出现次数,以及每次咳嗽出现的索引位置。
扩展(*):统计每个字符出现的次数。
private static void Main(string[] args)
{
string s = "患者:“大夫,我咳嗽得很重。\";大夫:\"你多大年龄?\";" +
"患者:\"七十五\r\n\t\t岁。\";大夫:\"二十岁咳嗽吗\";患者:\"不咳" +
"嗽。\";大夫:\"四十岁时咳嗽吗?\";患者:\"\r\n\t\t也不咳嗽。\";大" +
"夫:\"那现在不咳嗽,还要等到什么时咳嗽?\";";
int count = 0, n = 0, index = 0;
while ((index = s.IndexOf("咳嗽", index)) != -1)
{
count++;
Console.WriteLine("第{0}次出现的位置是{1}", count, index);
index += 2;
}
Dictionary<char, int> dic = new Dictionary<char, int>();
for (int i = 0; i < s.Length; i++)
{
if (dic.ContainsKey(s[i]))
{
dic[s[i]]++;
}
else
{
dic[s[i]] = 1;
}
}
foreach (KeyValuePair<char, int> item in dic)
{
Console.WriteLine($"{item.Key}:{item.Value}");
}
Console.ReadKey();
}
思路:
判断字符串中有且只有一个这样的字符: IndexOf()=LastIndexOf()
即从左找到的索引等于从右找的索引,且不能为-1。
14、将字符串" hello word,你 好 世界 ! "两端空格去掉,并且将其中的所有其
它空格都替换成一个空格,输出结果为:"hello world,你 好 世界!".
private static void Main(string[] args)
{
string s = " hello word,你 好 世界 ! ";
string[] s1 = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine(string.Join(" ", s1));
Console.ReadKey();
}
技巧:
调试时,执行到光标定位处:Ctrl+F10
15、制作一个控制台小程序。要求用户可以在控制台录入每个学生的姓名,当用户输入quit
(不区分大小写)时,程序停止接受用户的输入,并且显示出用户输入的学生的个数,以
及每个学生的姓名。
样例:请输入姓名:
张三
请输入姓名:
李四
....
quit
一共录入4个学生,姓名分别为张三,李四....
private static void Main(string[] args)
{
List<string> list = new List<string>();
string s = string.Empty;
do
{
Console.WriteLine("请输入姓名:");
s = Console.ReadLine();
list.Add(s);
} while (s.ToLower() != "quit");
list.RemoveAt(list.Count - 1);//最后一个quit去除
string[] s1 = list.ToArray();
Console.WriteLine($"一共录入学生{list.Count}个,分别是{string.Join(" ", s1)}");
Console.ReadKey();
}
16、同上,再增加一个姓"王"的同学的个数,此处不考虑复姓问题。
private static void Main(string[] args)
{
List<string> list = new List<string>();
string s;
do
{
Console.WriteLine("请输入姓名:");
s = Console.ReadLine();
list.Add(s);
} while (s.ToLower() != "quit");
list.RemoveAt(list.Count - 1);//最后一个quit去除
List<string> list2 = new List<string>();
foreach (string s1 in list)
{
if (s1.StartsWith("王"))
{
list2.Add(s1);
}
}
Console.Write("王姓同学共{0}个,分别是:", list2.Count);
foreach (string s1 in list2)
{
Console.Write(s + " ");
}
Console.ReadKey();
}
17、将普通日期格式:2011年6月4日转为:二零一一年六月四日。
暂不考虑10日,13日,23日等带十的问题。
private static void Main(string[] args)
{
Dictionary<char, char> dic = new Dictionary<char, char>() { { '0', '零' }, { '1', '壹' }, { '2', '贰' },
{ '3', '叁' },{ '4', '肆' }, { '5', '伍' }, { '6', '陆' }, { '7', '柒' }, { '8', '捌' }, { '9', '玖' },};
string s = "2011年6月4日", s1 = "";
for (int i = 0; i < s.Length; i++)
{
switch (s[i])
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
s1 += dic[s[i]];
break;
default:
s1 += s[i];
break;
}
}
Console.WriteLine(s1);
Console.ReadKey();
}
18、创建一个Person类,属性:姓名,性别,年龄;方法SayHi().再创建一个Employee类
继承Person类,扩展属性Salary,重写SayHi方法。
internal class Program
{
private static void Main(string[] args)
{
Employee p = new Employee() { Name = "张三", Gender = '男', Age = 28, Salary = 2342.23 };
p.SayHi();
Console.ReadKey();
}
public class Person
{
public string Name { get; set; }
public char Gender { get; set; }
public int Age { get; set; }
public void SayHi()
{
Console.WriteLine("Hello");
}
}
public class Employee : Person
{
public double Salary { get; set; }
public new void SayHi()
{
Console.WriteLine("hellow one");
}
}
}
提示:
属性并不能存储数据,自动属性的背后也是有一个隐藏的字段进行存储数据。
19、编写一个类:ItcastClass,类中有私有字段_name,字符串数组类型,长度5,并且有5个
默认的姓名。要求:为ItcastClass类编写一个索引器,要求索引器能够通过下标来访
问_names中的问题。
internal class ItcastClass
{
public int Count//返回索引器长度,只读
{
get
{
return _name.Length;
}
}
private string[] _name = { "苏坤", "蒋坤", "杨中科", "马伦", "赵剑宇" };//杨洪波
public string this[int n]
{
get
{
if (n >= 0 && n <= _name.Length - 1)
{
return _name[n];
}
throw new ArgumentException();
}
set
{
if (n < 0 || n >= _name.Length)
{
throw new ArgumentException();
}
_name[n] = value;
}
}
//public string this[string str]//索引器重载
//{
//}
//public string Item //已经有索引器Item了
//{
//}
}
分析:类中有数组5个为私有,不能直接访问,可通过其它如属性进行间接访问。
1)索引器用[]代替字段或属性的间接引用,直接用对象名加[]即可表达该值
2)索引器不能有具体的名称,应该用this进行代替。
3)索引器[]中的参数也应该进行设置。里面参数可以是多个,例如二维数组。
4)索引器[]中的参数应该在属性中进行过滤设置。
5)索引器可以重载,参数的类型个数可以不同。
5)索引器的count应单独设置一个属性(只读)
小结:
索引器其实就是一个属性,是一个非常特殊的属性,常规情况索引器其实都是一个
名叫Item的属性.通过反编译内部可以查看到。
因此如果在类中再写一个Item的属性,会报错。同理Count也必须是唯一的。
20、案例:使用WinForm窗体,制作一个简易计算器,默认为“请选择"。要求具有+-*/功能.
当用户点击“等于”按钮时,如果输入的为非数字则提示用户。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.Items.Add("请选择");
comboBox1.Items.Add("+");
comboBox1.Items.Add("-");
comboBox1.Items.Add("*");
comboBox1.Items.Add("/");
comboBox1.SelectedIndex = 0;
}
private bool IsNum(string str)
{
try
{
double d = Convert.ToDouble(str);
}
catch (Exception)
{
return false;
}
return true;
}
private void button1_Click(object sender, EventArgs e)
{
if (IsNum(textBox1.Text) && IsNum(textBox2.Text))
{
switch (comboBox1.Text)
{
case "+":
label1.Text = (Convert.ToDouble(textBox1.Text) + Convert.ToDouble(textBox2.Text)).ToString();
break;
case "-":
label1.Text = (Convert.ToDouble(textBox1.Text) - Convert.ToDouble(textBox2.Text)).ToString();
break;
case "*":
label1.Text = (Convert.ToDouble(textBox1.Text) * Convert.ToDouble(textBox2.Text)).ToString();
break;
case "/":
if (textBox2.Text == "0")
{
MessageBox.Show("除数不能为0");
return;
}
double d = Convert.ToDouble(textBox1.Text) / Convert.ToDouble(textBox2.Text);
d = Math.Round(d, 3);
label1.Text = d.ToString();
break;
default:
MessageBox.Show("请选择正确的运算符");
break;
}
}
else
{
MessageBox.Show("输入不是数字");
}
}
}
技巧:
如何查看vs自身函数的代码。例如查看Convert.ToInt32(string)
方法一:鼠标放在该函数中,直接按F12,即定位到对应的函数的代码中。
方法二:打开.Net Reflector(网上下载),点击工具栏的搜索,输入Convert,
回车,在右侧已经过滤的结果中双击Convert后,在左侧的对应的函数名
中找到ToInt32(string):Int32,点击它,则右侧下方出现对应的代码。
21、编写代码说明什么是方法重载。要求,至少编写3个方法。
private static void Main(string[] args)
{
int n = 3; double n1 = 4.0; string n2 = "5";
GetNum(n);
GetNum(n1);
GetNum(n2);
Console.ReadKey();
}
public static void GetNum(int n)
{
n++;
Console.WriteLine(n);
}
public static void GetNum(double n)
{
n++;
Console.WriteLine(n);
}
public static void GetNum(string n)
{
n += "1";
Console.WriteLine(n);
}
提示:
重载只看方法名与参数(个数与类型),与返回值无关。
22、请将字符串数组{"中国","美国","巴西","澳大利亚","加拿大"}中的内容反转,然后
输出反转后的数组。不能用数组的Reverse()方法.
private static void Main(string[] args)
{
string[] s = { "中国", "美国", "巴西", "澳大利亚", "加拿大" };
for (int i = 0; i < s.Length / 2; i++)
{
string temp = s[i];
s[i] = s[s.Length - 1 - i];
s[s.Length - 1 - i] = temp;
}
Console.WriteLine(string.Join(" ", s));
Console.ReadKey();
}
技巧:
做项目时,不再局限自己写算法,能用系统的方法就用。
比如本题可以用Reverse()时就用,毕竟都是经广大程序员使用认证的,效率更好。
后面数据库时再做下面的题:
23、请在数据库中新建一张表(T_Student),该表中有3列(自动编号,姓名,年龄),向
该表中手动插入若干条数据,然后通过ado.net将该表中的数据显示到程序中来,可以
使用DataGridView控件显示数据。
24、请写一个SQL语句:从user表中取出name列中的起始字符是"北京"的全部记录。
25、写出一条SQL语句:取出表A中第31到第40条记录(SQLServer以自动增长的ID作为主键)
注意:ID可能不是连续的。
三、代码规范
1、规则(如同法律,必须遵守否则报错。)
例如,语法。
2、规范(道德,大家都喜欢有道德的人)
注释:
Camel命名:普通变量(局部变量),字段
Pascal命名:方法名,类名,属性名。
定义变量或方法(名字要有意义)
方法名:一般动词开头,方法是做一件事:Write(),Open(),Display()
或者以返回值进行命名,例如得到ID:GetID().
或者以起源或目标命名,例如:GetFileFrom(),WriteToDoc()
变量名:按功能命名,按方法的返回值内容命名。
userName=GetUserName()
写不来英文就写拼音,但必须是全拼,长点都可以,以便识别。
例如:XieRuWenJian()写入文件,但不能简写成XRWJ()无法识别。
尽量写便于识别的代码,不推荐艰深的代码。
四、.Net程序基本编译执行流程C#
1、编写c#代码,保存为.cs文件。
2、通过csc.exe程序来将.cs文件编译为.Net程序集(.exe或.dll)。
此时的exe或dll并不是机器码(CPU不识别,不理解)
>csc /out:c:\a.exe c:\Program.cs
3、程序运行时通过JIT编译(Just In Time)即时编译,将程序集编译为cpu能理解的机器码,
这时cpu才能执行。这个编译过程会与当前的机器有关(根据当前的机器的内存cup等)
JIT程序运行到哪儿就编译到哪儿。例如,一个程序集有3个exe,本次只运行了1和3的
exe,那它就只编译1和3的exe,至于2的exe下次如果运行它,则再进行编译它。也即
并不是一股脑儿的全部编译。
五、代码片段
输入cw,再按两下Tab,则直接输出Console.WriteLine();
这个就叫代码片段,是快速输入代码的一种方式。
自定义代码片段
菜单中工具->代码片段管理器,弹出的窗体中选择语言CSharp,左下侧选择Visual C#
随便选择一个代码片段,比如do,选中它,上面有一个位置目录,复制目录,在windows
中打开这个目录,这里面就是代码片段保存的位置。
找到do.snippet,复制到另一个目录中打开它,更改里面的名称以及快速键,还有光标
停留位置,保存重命名为你的编辑的快速键。
然后在代码片段管理器的左下方有一个导入,导入上面保存的片段即可添加这个快捷
的代码片段。
(最近发现直接复制到visual C#目录中也可以直接使用,不必导入)