C#知识点整理综合
———————————————————数据类型————————————————
◆◆1.仓库与盒子的使用——数据类型与变量
(1)变量被用来存储特定类型的数据。
把应用程序的内存看做是一个仓库,把变量看做是存放数据的盒子。
(2)盒子的选择也就是变量的选择。
(3)变量的声明是指定变量的名称和类型。
①未经声明的变量本身并不合法
②变量必须初始化,即变量必须被赋值才能使用。
(4)变量的作用域就是可以访问该变量的代码区域。
(5)允许空值的变量 null
null不是0或空字符串,代表的意义是"未定义","未设定"
当允许一开始不设定初始值,也不用赋值的情况时,可以在数据类型后面加?号。
int?age=null;
◆◆2.数据类型之值类型的使用
(1)基于值类型的都分配在堆栈上,一旦离开其定义的作用域,立即就会从内存中删除。
(2)值类型数据:
①简单类型:所有的简单类型都是.Net Framework系统类型的别名,例如:int 是System.Int32的别名。
②结构类型:把一组相关的信息放在一起,把一系列相关的变量组成一个单一实体过程。
这个单一实体的类型就是结构类型。每一个变量称为结构的成员。
★★结构类型采用struct进行声明。
★★结构类型也是一种封装,即把相关事物的属性和方法封装在一起
简单的结构封装,同类一样可以带构造函数。
struct Employee
{
public string empname;
public int Age;
}
③枚举类型
(3)所有的值类型都隐式的继承自System.ValueType类,该类又继承自object类
所有值类型都是隐式密封的,在堆栈中进行分配,提高性能。
◆◆3.数据类型之引用类型的使用
所有被称为“类”的都是引用类型,包括类,接口,数组和委托等。
指向数据的指针或引用,引用类型为null时表示没有引用任何对象。
这些对象一直保存在内存中,直到.Net垃圾回收器将他们销毁。
对一个引用类型的赋值,将产生对该堆上同一个对象的新引用。
变量按引用进行传递
(1)结构类型,值类型
class Program
{
static void
{
int v1 = 0;
int v2 = v1;
v2 = 987;
Console.WriteLine("{0},{1}", v1, v2);
C c1 = new C();
C c2 = c1;
c2.value = 987;
Console.WriteLine("{0},{1}", c1.value, c2.value); 0,987
Console.ReadKey();
}
}
struct C //结构类型,值类型
{
public int value;
}
(2)声明了一个类,引用类型
class Program
{
static void
{
int v1 = 0;
int v2 = v1;
v2 = 987;
Console.WriteLine("{0},{1}", v1, v2);
C c1 = new C(); 对类的引用
C c2 = c1;
c2.value = 987;
Console.WriteLine("{0},{1}", c1.value, c2.value); 987,987
Console.ReadKey();
}
}
class C //声明了一个类,引用类型
{
public int value;
}
★★★★尽管值类型和引用类型存在着差异,但是二者都有实现接口的能力,支持任意数量的字段,属性,事件和常量。
———————————————————if for switch的使用————————————
◆◆4.岔路口的选择——if语句的使用
只有()里的内容为被满足为真的情况下才会执行{}里的代码。
如果光一个if(){},那么存在着缺陷,即如果不满足条件,就不会做任何处理,也不返回任何结果。
而使用了
if()
{}
else()
{}
则可避免这个缺陷。
★★★★if语句的嵌套使用。
◆◆5.春夏秋冬——switch的使用
(1)switch 语句后面可以跟 sbyte,byte,short,ushort,int,uint,long,ulong,char,string和枚举类型。
(2)每个case后面跟着的【常量表达式】必须与【表达式】的类型相同,且是一个常量,不能是变量.
(3)两个或多个case指定同一个常数值,就会导致编译出错。
◆◆6.for语句,如果不设置循环条件,程序就会产生死循环。
for(初始值;不尔表达式;更新值)
{
语句块
}
下面来看一个无限循环的例子
int i = 0;
for (; ; )
{
i++;
Console.WriteLine(i);
}
———————————————————数组的使用———————————————
◆◆7.数组
(1)包含相同类型变量的集合,所有成员必须具有相同的类型
(2)这些变量可以通过索引进行访问,数组的索引从0开始
(3)数组中的变量称为数组的元素。元素都具有唯一的索引与之相对
(4)数组能够容纳元素的数量称为数组的长度。
(5)数组的维数即数组的
(6)数组类型是从System.Array派生的引用类型,可分为一维,多维和交错数组。
★★二维数组的使用★★★
(1)二维数组的声明
★★.Net索引值一律是从0开始的。
int[,]=new int[4,5]
第一维的长度是4,索引的下标是0 1 2 3
第二维的长度是5,索引的下标是0 1 2 3 4
(2)
int[,] inti = new int[4, 5] { { 1, 2, 3, 4 ,5}, { 1, 2, 3, 4,5 }, { 1, 2, 3, 4,5 }, { 1, 2, 3, 4,5 }};
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 5; j++)
{
Console.WriteLine(inti[i, j]);
}
}
(3)清除个别数组元素以外的其他成员Array.Clear(清除的数组元素,起始索引,要清除的个数)
string[] s = { "张三", "王俊", "刘一江" };
Array.Clear(s, 1, 2);
foreach (string s
Console.WriteLine(s1); //清除数组s中,索引为1的元素,清除2个,最后输出 ""
Console.ReadKey();
———————————————————字符串的比较——————————————
◆◆8.字符串的比较
(1)判读两个字符串是否一致,即一摸一样
string s1 = "abcd";
string s2 = "ABcd";
int i = s1.CompareTo(s2);
if (i == 0)
{
Console.WriteLine("相等");
}
else
{
Console.WriteLine("不相等");
}
(2)用Equals进行比较
string s = "abcdefg";
Console.WriteLine(s.Length);
//②比较两个字符串的范围,忽略大小写,字符顺序是否一样
//s.Equals(new string,StringComparison.OrdinalIgnoreCase)
if (s.Equals("abcdEFG", StringComparison.OrdinalIgnoreCase))
Console.WriteLine("这是两个一样的字符串");
else
Console.WriteLine("两个字符串不一致");
//③比较两个字符串大小写是否一样 s.Equals(newstring)
if (s.Equals("abcdefg")) //两个字符串如果相同则返回True值
{
Console.WriteLine("两个字符串一样");
}
else
{
Console.WriteLine("两个字符串不一样");
}
(3)字符串对比一般都用: if(str1==str2){ } , 但还有别的方法:
①string str1; str2
//语法: str1.EndsWith(str2); __检测字串str1 是否以字串str2 结尾,返回布尔值.如:
if(str1.EndsWith(str2)){ Response.Write("字串str1 是以"+str2+"结束的"); }
②
//语法:str1.Equals(str2); __检测字串str1 是否与字串str2 相等,返回布尔值,用法同
上.
③
//语法Equals(str1,str2); __检测字串str1 是否与字串str2 相等,返回布尔值,用法同
上.
(4)比较两个数的大小
比较两个数的大小 System.Math.Max(i, j)
int i = 10;
int j = 5;
int x = System.Math.Max(i, j);
MessageBox.Show(x.ToString());
———————————————————字符串函数———————————————
◆◆9.字符串函数的专讲
(1)常用的一些字符串函数
//①给出一个字符串,首先要测试出这个字符串的长度
//用length 还可以"" 字符串的长度,null则无法测试出长度
//因为null不指定存储空间,所以无法测试长度
//length 1个汉字占一个字节
string s = "abcdefg";
Console.WriteLine(s.Length);
//②比较两个字符串的范围,忽略大小写,字符顺序是否一样
//s.Equals(new string,StringComparison.OrdinalIgnoreCase)
if (s.Equals("abcdEFG", StringComparison.OrdinalIgnoreCase))
Console.WriteLine("这是两个一样的字符串");
else
Console.WriteLine("两个字符串不一致");
//③比较两个字符串大小写是否一样 s.Equals(newstring)
if (s.Equals("abcdefg")) //两个字符串如果相同则返回True值
{
Console.WriteLine("两个字符串一样");
}
else
{
Console.WriteLine("两个字符串不一样");
}
//④字符串劈开split函数
//1)简单的字符串劈开函数 s1.Split('|')
★分开的是数组,而不是字符串
string s1 = "aa|bb|c";
string[] f = s1.Split('|'); //是'' 而不是" "
foreach (string e in f)
Console.Write(e);
Console.WriteLine();
//2)劈开一个字符,组成字符串数组
//RemoveEmptyEntries移除空的字符串数组元素 none 保留空的字符串数组元素。
string[] f1 = s1.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string e
Console.WriteLine(e1);
//3)string[] f3 = s.Split('我') 只能劈开单个字符
string[] f2 = s1.Split(new string[] { "我是" }, StringSplitOptions.RemoveEmptyEntries);
foreach (string e
Console.WriteLine(e2);
//⑤敏感字符的判断
//1)字符串中是否有子串 是否有敏感字符串 s.Contains("社会") || s.Contains("和谐")
string str1 = "我们的社会真和谐啊!!!";
if (str1.Contains("社会") || str1.Contains("和谐"))
Console.WriteLine("禁止出现敏感词汇");
//2)敏感词汇进行*掩藏
string str = "社会真和谐啊,构造和谐社会!!!";
if (str.Contains("和谐"))
{
char[] temp = str.ToCharArray();
for (int i = 0; i < temp.Length; i++)
{
if (temp[i] == '和' && temp[i + 1] == '谐')
{
temp[i] = '*';
temp[i + 1] = '*';
}
}
string s3 = new string(temp);
Console.WriteLine(s3);
Console.ReadKey();
}
//⑥字符串大小写转换 ToLower() ToUpper()string strbs = "abcde";
Console.WriteLine(strbs.ToLower());//全部转换成小写的形式
Console.WriteLine(strbs.ToUpper());//全部转换成大写的形式
//⑦修剪字符串中出现的空格
string strspace = " aaa bbb ccc ";
Console.WriteLine("原始字符串是:{0}|", strspace);
Console.WriteLine("没有修剪之前的字符串长度,{0}", strspace.Length);
Console.WriteLine("\n");
Console.WriteLine("修剪两边字符串后是:{0}|", strspace.Trim());
Console.WriteLine("修剪两边字符串后的长度,{0}", strspace.Trim().Length);
Console.WriteLine("\n");
Console.WriteLine("修剪字符串开始位置后是,{0}|", strspace.TrimStart());
Console.WriteLine("修剪字符串开始位置后长度,{0}", strspace.TrimStart().Length);
Console.WriteLine("\n");
Console.WriteLine("修剪字符串结束位置后是,{0}|", strspace.TrimEnd());
Console.WriteLine("修剪字符串结束位置后长度,{0}", strspace.TrimEnd().Length);
//⑧字符串替换函数 s.Replace(oldstring,newstring ); 字符串替换
Console.WriteLine("\n");
string strreplace = "高山|长江|黄河|北京";
strreplace = strreplace.Replace("|", ",");
Console.WriteLine(strreplace); //高山,长江,黄河,北京
//⑨
//s.Substring(startIndex, length) 开始的索引 截取长度
string strsub = "高山|长江|黄河|北京";
strsub = strsub.Substring(3, 3);
Console.WriteLine(strsub);
//三元运算符 i==5?"是":"否"
int inti = 5;
string strsub2 = (inti == 5 ? "是" : "否");
Console.WriteLine(strsub2);
string strsub3 = "高山|长江";
strsub3 = strsub3.Substring(2, strsub3.Length - 2 > 5 ? 5 : strsub3.Length - 2);
// 或者 s = s.Substring(2,Math.Min(s.Length-2,5));
Console.WriteLine(strsub3);
Console.ReadKey();
Equals判断对象是否想等。
如果不在指向同一存储地址的话,那么肯定不想等,如果指向同一内存地址的话,再进行判断。
字符串进行判断的是两个对象的值,而不是对象。
// test t = new test();
t.Name = "a";
test t1 = new test();
t1.Name = "a";
t.Equals(t1);
class test
{
public string Name;
public override bool Equals(object obj)
{
return this.Name == ((test)obj).Name;
}
}
(2)ToString()的使用
12345.ToString("n"); //生成 12,345.00
12345.ToString("C"); //生成 ¥12,345.00
12345.ToString("e"); //生成 1.234500e+004
12345.ToString("f4"); //生成 12345.0000
12345.ToString("x"); //生成 3039 (16进制)
12345.ToString("p"); //生成 1,234,500.00%
(3)如何在指定范围内产生随机函数 ran.Next(10, 20)
Random ran = new Random();
textBox1.Text = Convert.ToString(ran.Next(10, 20));
(4)日期函数
DateTime 数字型
System.DateTime currentTime=new System.DateTime();
1.1 取当前年月日时分秒
currentTime=System.DateTime.Now;
1.2 取当前年
int 年=currentTime.Year;
1.3 取当前月
int 月=currentTime.Month;
1.4 取当前日
int 日=currentTime.Day;
1.5 取当前时
int 时=currentTime.Hour;
1.6 取当前分
int 分=currentTime.Minute;
1.7 取当前秒
int 秒=currentTime.Second;
1.8 取当前毫秒
int 毫秒=currentTime.Millisecond;
(5)在指定的字符串中 插入函数Insert()
在字串中指定索引位插入指定字符。如:
str1.Insert(1,"字");在str1 的第二个字符处插入“字”,如果str1="中国",插入后
为“中字国”;
(6)在字串左(或右)加空格或指定char 字符,使字串达到指定长度
PadLeft()、PadRight()
<%
string str1="中国人";
str1=str1.PadLeft(10,'1'); //无第二参数为加空格
Response.Write(str1); //结果为“1111111 中国人” , 字串长为10
%>
———————————————————可空类型与全角字符转换为半角——————
◆◆10.一无所有——可空类型
C#通过在数据类型关键字后面紧跟着符号?可以定义可空类型,即允许该变量类型有空值
int?x=null;
char?[] ch=new char?[]{'a',null,'b'};
(2)C#还提供了一个二元运算符为可空类型赋值
int? x=null;
int? y=x??5; //如??左边的值为null,那么返回??右边的值,否则返回左边的值
(3)int? 等价于 System.Nullable<int>
◆◆11.半壁江山——全角字符转换为半角
(1)全角字符:一个字符占两个标准字符位置,汉字字符,规定了全角的英文字符,特殊字符,图形符号都是全角字符。
1 2
(2)半角字符
一字符占用一个标准的字符位置,通常的英文字母,数字,符号都是半角的。以ASCII方式的字符。
123abc?
——————————————————C#高级语法————————————————
◆◆12.编写高质量的C#程序
(1)如何进行换行
①某两个表达式项之间将其断开
if (f == ImageFormat.Jpeg.Guid ||
f == ImageFormat.Tiff.Guid ||
double containerAspectRatio =
(double)container.ClientWidth / container.ClientHeight;
Rectangle imageBounds = new Rectangle(
itemBounds.X + padding,
②编译器总是按行来进行设计的,将多条语句写在一行会引起不必要的麻烦
③使用空行分隔代码块
④使用空格降低代码密度
⑤在每一行的代码左端空出一部分长度,更加清晰地从外观上体现出程序的层次结构
⑥在“选项”对话框中对C# 编辑器的代码缩进方式进行设置(如图1-2),选择“插入空格”模式。
◆◆13.c#中布尔类型的值true和false都是小写的包括头字母
◆◆14.C#中using创建一个别名
using Con = System.Console;
Con.WriteLine("123"); //使用你创建的别名
Con.ReadKey();
◆◆15.C#不允许使用关键字作为标识符。虽然使用关键字作为标识符名称是不可取的但有时使用
其他语言的代码可能会需要使用关键字作为标识符。在这种情况下可以在关键字前面加一个
@符号用在标识符。
int @int = 10;
MessageBox.Show(@int.ToString());
◆◆16.获取本机的IP地址
System.Net.IPAddress[] addressList = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
String strIp = addressList[0].ToString();
MessageBox.Show(strIp);
◆◆17.//获取应用程序运行路径
string myAppPath = Application.ExecutablePath; //获取当前程序的路径
int nIndex = myAppPath.LastIndexOf(@"\"); //找到最后一个\的索引
myAppPath = myAppPath.Substring(0, nIndex); //从第0个到最后一个的索引就是本程序的路径
MessageBox.Show(myAppPath);
◆◆18.OpenFileDialog的使用
//设置初始目录
openFileDialog.InitialDirectory = strPath;
//设定可选择的文件类型
openFileDialog.Filter = "图标|*.ico|Bitmap图像|*.bmp|JPEG图像|*.jpg";
//默认的文件类型
openFileDialog.FilterIndex = 1;
openFileDialog.RestoreDirectory = false;
//第一个或者最后一个文件的名称
openFileDialog.FileName = "";
if (this.openFileDialog.ShowDialog() == DialogResult.OK)
◆◆19.进度条的显示
progressBar1.Value = 30;//表示进度为30%
◆◆20.使系统发出蜂鸣声、报警声等
SystemSounds.Beep.Play();//(Beep / Exclamation / Question / Head);
◆◆21.程序集
Assembly 一堆类打一个包
.Net中有程序集的概念,托管代码 在.Net framework中运行,可以进行反编译
★★★程序中只引用必须的程序集,减小程序的尺寸
一些程序集得内部类不允许外部进行访问. internal
★★★只能单向进行引用,不能进行圈式引用 即 A引用B B引用A
◆◆22.全局程序集 GAC
◆◆23.垃圾回收
垃圾回收解放了手工管理对象的工作,提高了程序的健壮性,但副作用就是程序代码可能对于对象创建变得随意。
(1)避免不必要的对象创建★★★★★避免创建不必要的对象
由于垃圾回收的代价较高,所以C#程序开发要遵循的一个基本原则就是避免不必要的对象创建。以下列举一些常见的情形。
(2)避免循环创建对象 ★★★★★创建对象的时候不要在循环中进行创建
如果对象并不会随每次循环而改变状态,那么在循环中反复创建对象将带来性能损耗。高效的做法是将对象提到循环外面创建。
(3)在需要逻辑分支中创建对象 ★★★★★懂得对象在作用域中创建,一离开作用域就释放
如果对象只在某些逻辑分支中才被用到,那么应只在该逻辑分支中创建对象。
(4)使用常量避免创建对象
程序中不应出现如 new Decimal(0) 之类的代码,这会导致小对象频繁创建及回收,正确的做法是使用Decimal.Zero常量。我们有设计自己的类时,也可以学习这个设计手法,应用到类似的场景中。
——————————————String的操作———————————————————
◆◆24.String 操作
(1)String是不变类,使用 + 操作连接字符串将会导致创建一个新的字符串。如果字符串连接次数不是固定的,例如在一个循环中,则应该使用 StringBuilder 类来做字符串连接工作。因为 StringBuilder 内部有一个 StringBuffer ,连接操作不会每次分配新的字符串空间。只有当连接后的字符串超出 Buffer 大小时,才会申请新的 Buffer 空间
StringBuilder sb = new StringBuilder(256 );
for ( int i = 0 ; i < Results.Count; i ++ )
{
sb.Append (Results[i]);
}
如果连接次数是固定的并且只有几次,此时应该直接用 + 号连接,保持程序简洁易读。实际上,编译器已经做了优化,会依据加号次数调用不同参数个数的 String.Concat 方法。例如:String str = str1+ str2 + str3 + str4;
(2)避免不必要的调用 ToUpper或 ToLower 方法
String是不变类,调用ToUpper或ToLower方法都会导致创建一个新的字符串。如果被频繁调用,将导致频繁创建字符串对象。这违背了前面讲到的“避免频繁创建对象”这一基本原则。
例如,bool.Parse方法本身已经是忽略大小写的,调用时不要调用ToLower方法。
另一个非常普遍的场景是字符串比较。高效的做法是使用 Compare 方法,这个方法可以做大小写忽略的比较,并且不会创建新字符串。
还有一种情况是使用 HashTable 的时候,有时候无法保证传递 key 的大小写是否符合预期,往往会把 key 强制转换到大写或小写方法。实际上 HashTable 有不同的构造形式,完全支持采用忽略大小写的 key: new HashTable(StringComparer.OrdinalIgnoreCase)。
(3)最快的空串比较方法
将String对象的Length属性与0比较是最快的方法:if (str.Length == 0)其次是与String.Empty常量或空串比较:if (str == String.Empty)或if (str == "")
注:C#在编译时会将程序集中声明的所有字符串常量放到保留池中(intern pool),相同常量不会重复分配。
(4)拼装字符串
if (str.Length == 0 )
str = vals[i].ToString();
else
str += " , " + vals[i].ToString();
其实这种写法非常自然,而且效率很高,完全不需要用个Remove方法绕来绕去。
(5)避免两次类型转换
———————————C#中readonly与const的区别———————————————◆◆25.C#中readonly与const的区别
(1)他们都是常量类型 readonly 运行时常量 const编译时常量
(2)readonly也称为只读变量
(3)工作原理(readonly保持引用方式,而const则替换成常量)
public static readonly int A = 2; //A为运行时常量
public const int B = 3; //B为编译时常量 下面的表达式:
int C = A + B;
int C = A + 3;
经过编译后与下面的形式等价:
可以看到,其中的const常量B被替换成字面量3,而readonly常量A则保持引用方式。
(4)声明及初始化
readonly 只能声明为类字段,支持实例类型或静态类型
可以在声明的同时初始化,或在函数中进行初始化
const出了可以声明为类字段之外,还可以声明为方法中的局部常量。
默认为static,无需进行修饰,但必须在声明的时候进行初始化.
(5).数据类型支持
由于const在编译时替换成字面量,所以取值受一定的限制,日期类型无法进行声明
而readonly则可以进行声明
public const DateTime D = DateTime.MinValue; 改成readonly就可以正常编译:
(6)可维护性
readonly 以引用方式进行工作,某个常量更新后,所有引用该常量的地方都能得到更新后的值
const则无法做到这点
在下面两种情况下:
a.取值永久不变(比如圆周率、一天包含的小时数、地球的半径等)
b.对程序性能要求非常苛刻
可以使用const常量,除此之外的其他情况都应该优先采用readonly常量。
————————————————C#面向对象——————————————————
◆◆26.枚举
//限定变量的取值范围,确定个数 bool 类型可以用枚举,只是.Net内置了bool类型,所以没人这么做
//QQ的在线,隐身。。
static void
{
Gender g = Gender.Fremal;
Console.WriteLine(g);
Gender g1 = Gender.Man;
Console.WriteLine(g1);
Console.ReadKey();
}
enum Gender { Man,Fremal,Unkown}
◆◆27.函数
//对一堆代码进行重用的一种机制,可能有输入的值(参数),可能有返回的值
返回值: Console.ReadLine(); 读取后有一个返回的值
参数: Console.WriteLine(Console.ReadLine());
// int i = Convert.ToInt32("123");
(1)有声明返回值的
static void
{
int inti = Readint(); //接收函数返回的值
Console.WriteLine(inti);
Console.ReadKey();
}
static int Readint()
{
return 30; //return后跟上函数返回的值
}
(2)没有声明返回值的 void
static void
{
int inti = Readint();
SayHellow();
Console.WriteLine(inti);
Console.ReadKey();
}
static void SayHellow()
{
Console.WriteLine("abc");
return;
return 100; //报错,由于是void所以报错。
}
(3)如果我想要通过void返回值,怎么办?
1)参数是字符类型string
static void
{
string s = "Hellow";
Str(s); //按值传递,外部不影响内部的值
Console.WriteLine(s);
Console.ReadKey();
}
static void Str(string strs)
{
strs = "abc";
}
2)如果想影响的话,如何进行?
//参数的前面加上ref 按照引用传递,内部的值影响外部的值。
//①传递字符类型
static void
{
string s = "Hellow";
string s1 = Str(ref s);
Console.WriteLine(s1);
Console.ReadKey();
}
static string Str( ref string strs)
{
strs = "abc";
return strs;
}
//②传递int类型
static void
{
int a = 100;
SayHellow(a); // void—int 不能用隐式转换,必须显示转换.
Console.WriteLine(a);
Console.ReadKey();
}
static void SayHellow(int j)
{
j = 10;
}
(4) 下列关于构造函数的描述正确的是(c)理解构造函数和函数重载的区别
a)构造函数可以声明返回类型。//构造函数连返回值都没有,如何声明返回类型?
b)构造函数不可以用private修饰
c)构造函数必须与类名相同 //这个是对的。
d)构造函数不能带参数 //没有参数和不能带参数是两回事
class A
{
public A()
{
Console.WriteLine("钩爪子");
}
}
★★★首先理解如何构造函数(构造函数和函数重载不是一回事,构造函数是名称和类名一样的特殊函数
而函数重载是几个相同函数名使用了一些规则后,可以同时存在)
构造函数是用来创建对象的特殊函数,函数名和类名一样,没有返回值,连void都不用
构造函数可以有参数,默认情况下是没有参数的构造函数,记住没有没有参数和不能有参数是两回事
(5)函数的重载,就是函数的重名,
//只有参数的类型、顺序不一致才能函数重名,函数返回值类型一致与否没关系
//构成重载的条件:参数类型不同或者参数个数不同(不严谨的),与返回值无关。
(1)static void
{
SayHellow(123);
Console.ReadKey();
}
static void SayHellow(string a)
{
Console.WriteLine("您好啊!!!");
}
static void SayHellow(int n)
{
Console.WriteLine(102);
}
(6)理解什么是可变参数 以不确定个数的数组方式进行传递的就是可变参数。
★★★可变参数就是参数的
可变参数params 可变参数必须放到最后,即最后一个参数是可变参数
个数不确定的可变参数以数组的形式传递
static void
{
Inta(10, "11", "22", "33", "44");
Console.ReadKey();
}
static void Inta(int i, params string[] s) //可变参数必须放到最后一位,因为确定的参数传递完后,剩下的都是可变参数,否则则无法进行确定。
{
foreach (string a in s)
Console.WriteLine(a);
Console.WriteLine(i);
}
(7)如何理解对象的引用
int datetime,bool,char等类型都属于值类型(ValueType),赋值的时候是传递拷贝
普通的对象则是引用类型,赋值的时候是传递引用。我指向谁,兄弟你也来指向。
★★一个对象会占很大的内存,是大家共用一份。
◆◆28.如何理解 类的概念?抽象、摸不到的 而对应的具体的是对象。
面向对象 = 对象 + 类 + 继承 + 通信 。如果一个软件系统是使用这样四个概念来设计的,我们认为这个软件系统是面向对象的。
◆◆29.对使用可访问性级别的限制 ★★★基类的可访问性必须大于子类的可访问性
(1)基类的可访问性必须不小于派生类。
(2)~~~~派生类的可访问性必须不大于基类。
(3)~~~~成员的可访问性至少是包含它的类。
例子:
Class BaseClass{};
Public class MyClass: BaseClass{};
这是错误的!!!!!!!!!!!!!!!!
类是个抽象的,对一些具有相同特征事物的一个抽象,比如一个班有同学,是一个抽象,具体到某
一个人是对象,笔记本电脑就是一个类,具体某一个人的笔记本电脑是对象。
类不占内存,对象才占内存。
static void
{
Person per = new Person();
per.name = "张三";
per.age = 25;
per.height = 180;
Console.WriteLine("姓名为:{0},年龄为:{1},身高为{2}CM", per.name, per.age, per.height);
Console.ReadKey();
}
class Person
{
public string name { get; set; }
public int age { get; set; }
public int height { get; set; }
}
◆◆30.如何不使用Public 声明字段,被调用?
为什么要用属性来声明,是为了在类中控制非法或合法值
而用public字段则无法进行合法值的判断
属性不会存储值,而是由相应的字段保存值
属性后不要加()
//用简写方式不能只有get或者只有set。不合理
//public int Height
//{
// get
//}
★★
a.SetAge(20);
Console.WriteLine(a.GetAge());
public int GetAge()
{
return age;
}
public void SetAge(int _age)
{
this.age = _age;
}
★★也可以这样调用类得方法
int t = a.SetAge(40);
Console.WriteLine(t);
public int SetAge(int _age)
{
if (_age > 30)
this.age = _age;
else
{
this.age = 100;
}
return this.age;
}
(1)可以用字段来接收属性设置的值,并进行返回
private string name1;
public string Name1
{
get
{
return this.name1;
}
set
{
value = "理解万岁";
this.name1 = value; //字段的值接收方法的值,可以对方法进行设置
}
}
(2)先通过方法进行更改,然后再通过属性进行返回值的操作。
p.giveName("张三"); //先把值传递给方法,然后再进行接收
Console.WriteLine(p.Name); p.属性名
private string name;
public string Name //属性再去接收方法赋予字段里的值
{
get
{
return this.name;
}
}
public void vname(string sname) //先接收传过来的值,然后进行修改 字段里接收方法赋予的值
{
if (sname == "张三")
{
sname = "李四";
this.name = sname;
}
}
◆◆32.如何理解继承?
(1)简单的 子类继承父类,其实也就是子类指向子类继承父类的字段,属性,方法。
class Progra
{
static void
{
B b = new B();
b.Say();
Console.ReadKey();
}
}
class A
{
public string name { get; set; }
public int age { get; set; }
public void Say()
{
Console.WriteLine("您好啊");
}
}
class B : A{ }
(2)如果想让父类指向子类,该如何做?
class Progra
{
static void
{
A a = new A();
B b =(B)a; //这样就会报错,如果不想报错的话,可以用as B b =a as B;
// B b1 = a as B;
Console.ReadKey();
}
}
(3)现在我想让父类调用子类里的方法,如何进行操作?要进行方法重写
★★使用虚方法,就是使用 virtual 关键字来进行修饰,从而达到“多态”的目的
方便以后对代码进行进一步完善。
★★子类可以不进行重写。
class Program
{
static void
{
//父类要先指向子类,才能用抽象的方法进行调用
A a = new B();
a.Say();
Console.ReadKey();
}
}
class A
{
public string name { get; set; }
public int age { get; set; }
public virtual void Say()
{
Console.WriteLine("001");
}
}
(4)除了使用上面的方法,还有没有使子类进行方法重写的呢?
答案是有的,可以使用抽象的方法,进行子类的重写,abstract
★★★就是为了子类去继承,不能new,不能实例化的
static void
{
//父类要先指向子类,才能用抽象的方法进行调用
A a = new B();
a.Say();
Console.ReadKey();
}
abstract class A
{
public string name { get; set; }
public int age { get; set; }
abstract public void Say(); //★★★这里声明抽象的,那么类也要是抽象的。
}
class B : A
{
public override void Say()
{
Console.WriteLine("子类说Say");
this.name = "张三丰";
Console.WriteLine("我叫张三丰");
}
}
(5)★★父类用 abstract 子类必须进行重写 子类也不能用new。
★★父类用virtual 子类可以用new不进行重写,那么就只能自己使用,不允许调用和继承。
(6)如果父类声明一个属性或字段,只想在本类中进行使用,不允许别人进行访问的话,可以用private
★★声明private的话,子类也不允许访问,继承。
那么如果只想子类进行继承,不允许别人访问的话,用用protected,相当于遗产保护。
protected void Name()
{
Console.WriteLine("输出姓名");
}
◆◆33.如何理解成员访问级别
(1)理解什么是成员?
★★字段、方法、属性 都可以叫做类的成员 都需要定义访问级别
访问级别的用处在于控制成员在哪些地方可以被访问,这样达到面向对象中“封装”的目的。
public(任何地方都可以访问);
private(默认级别。只能由本类中的成员访问)
(1)
p.Height = 180;
class Person
{
private string name { get; set; }
public int age { get; set; }
private int height;//外部程序无法调用private 声明的height
public int Height
{
get
{
return this.height; //内部使用的话,需要
}
set
{
this.height = Height;
}
}
}
◆◆34.如何理解全局变量(static)与变量,常量之间的区别。
变量与常量的区别
(1)常量
1)对于任何的对象的值都不变,所以不需要通过对象来调用,直接通过类名来引用。 类名.常量名
常量一般全部大写,不能进行赋值。
private const double P1 = 3.14;
static void
{
// P1 = 5; 常量不能进行赋值
Console .WriteLine(P1);
M1();
Console .ReadKey();
}
static void M1()
{
Console.WriteLine(P1*2);
}
2)PI.P1 类名.常量名
class Program
{
static void
{
// P1 = 5; 常量不能进行赋值
Console.WriteLine(PI.P1);
M1();
Console.ReadKey();
}
static void M1()
{
Console.WriteLine(PI.P1 * 2);
}
}
class PI
{
public const double P1 = 3.14;
}
(3)变量 程序易读,一个地方变了,所有地方都变了。
1)以字母或者下划线_开头,后面可以是任意字母,数字或下划线_,但不能是关键字(如class,new,int 在VS中蓝色的为关键字)。
int a3=9;
int _a3=9;
2)变量在声明后才能使用
int i3=10;
Console.WriteLine(i3);
变量在使用之前必须赋值
int i1;
i1 = 10;
Console.WriteLine(i1);
3)变量可以任意进行赋值,如需通过对象来调用的话,那么需要调用类里的属性或方法才能进行使用。
static void
{
Person p1 = new Person();
p1.age = 25;
p1.height = 175;
p1.Name = "张三丰";
Console.WriteLine("姓名为:{0},年龄为:{1},身高为:{2}",p1.Name,p1.age,p1.height);
Console.ReadKey();
}
class Person
{
public int age; //声明名称为age的变量
public int height;
public string Name //声明一个名称为Name的属性
{
get;
set;
}
}
//或者这样声明也是可以的。
class Person
{
public int age;
public int height;
private string name;
public string Name //声明一个名称为Name的属性
{
get{ return this.name; }
set{ this.name = value; }
}
}
(4)全局变量static 调用非static成员必须通过对象
class Program
{
static void
{
St a = new St();
a.Hellow();
St.Say();//静态static 的只能 类名.方法名
Console.ReadKey();
}
}
class St
{
public void Hellow()
{ }
static public void Say()
{ }
}
★★没有对象,即方法名前声明了 static ,那么只能类名.方法名,
★★如果有对象,即方法名前没有声明 static,那么要用一个变量指向类,然后用变量名.方法名
class Program
{
static void
{
St.age = 15;
Console.WriteLine(St.age);
Console.ReadKey();
}
}
static class St
{
public static int age;
}
下面是两段综合的面向对象的代码
(1)class Program
{
static void
{ //父类实例化,调用父类的方法
A a = new A();
a.Say();
//子类实例化,调用子类的方法
B b = new B();
b.Say(); //假设子类的方法,new 了,不允许父类进行调用的话,那么父类调用自己的方法
//父类指向子类,父类调用子类的方法父类用virtual,子类用override进行方法的重写
a = b;
a.Say();
a.Hellow();
//子类继承父类的属性,字段,方法
b.name = "张中秋";
Console.WriteLine("{0}", b.name);
//子类B的子类C继承A,B可以继承的方法,字段,属性
//C c = new C();
//c.Age = 18;
//Console.WriteLine("子类C可以继承父类A的{0}", c.Age);
//c.Tel(); //不允许访问父类中私有的private 字段
//如果想调用父类的限制成员访问级别的
//那么要在子类的方法中调用父类的,且父类的必须以protected进行声明 ,转到①
b.Hellow();
//继承抽象的父类
D d = new D();
d.name = "高明光";
Console.WriteLine(d.name);
d.CSay();
//无法调用抽象的子类
//C c = new C(); //无法创建抽象的类
//a = c;
Console.ReadKey();
}
}
class A
{
public string name { get; set; }
private int age;
public int Age
{
get { return this.age; }
set { this.age = value; }
}
public int height
{
get;
set;
}
public virtual void Say()
{
Console.WriteLine("父类说您好啊 !!!");
}
public virtual void Hellow()
{
Console.WriteLine("父类说Hellow");
}
private string Tel
{
get;
set;
}
protected string School
{
get;
set;
}
public virtual void CSay()
{
Console.WriteLine("父类可以调用抽象的子类");
}
}
class B : A
{
public new void Hellow()
{
Console.WriteLine("子类说Hellow2");
//①
this.School = "传智播客";
Console.WriteLine("父类不能直接调用protected,所以我只能在方法中调用{0}", this.School);
}
public override void Say()
{
Console.WriteLine("子类说您好Hellow");
}
}
abstract class C : B
{
public override void CSay()
{
Console.WriteLine("C是抽象的类,不允许被直接调用,只能被继承");
}
}
class D : C
{
}
(2.1)父类和子类都实例化,指向自己,那么都先调用自己的方法
如果子类没有该方法的话,那么子类才继承父类的方法,否则子类调用自己的方法。
class Program
{
static void
{
A a = new A();
a.Say(); //父类调用父类的方法
B b = new B();
b.Say(); //子类调用子类的方法,如果子类没有这个方法,才继承父类的方法
Console.ReadKey();
}
}
class A
{
public void Say()
{
Console.WriteLine("父类说");
}
}
class B : A
{
//public void Say() //子类如果有方法的话,调用子类自己的方法。
//{
// Console.WriteLine("子类说");
//}
}
(2.2)如果父类指向子类,想调用子类的方法。
(1)那么父类的方法必须声明virtual,子类的方法必须声明为override。
(2)父类是先找到自己本身的方法,如果是virtual进行声明的,
那么看看子类是不是override,进行声明的,如果子类是override进行声明的,那么父类可以调用子类的方法。
class Program
{
static void
{
A a = new B();
a.Play(); //调用的是子类的方法
Console.ReadKey();
}
}
class A
{
public virtual void Play()
{
Console.WriteLine("父类说飞起来了");
}
}
class B : A
{
public override void Play()
{
Console.WriteLine("子类说飞起来了");
}
}
如果父类是virtual进行声明的,而子类不是override进行声明的,那么父类还是调用自己的方法。
class Program
{
static void
{
A a = new B();
a.Play(); //调用的还是父类的方法
Console.ReadKey();
}
}
class A
{
public virtual void Play()
{
Console.WriteLine("父类说飞起来了");
}
}
class B : A
{
public void Play()
{
Console.WriteLine("子类说飞起来了");
}
}
如果父类和子类前面没有任何修饰符的话,那么父类虽然指向子类,仍是调用父类的方法。
class Program
{
static void
{
A a = new B();
a.Play(); //调用的是父类的方法
Console.ReadKey();
}
}
class A
{
public void Play()
{
Console.WriteLine("父类说飞起来了");
}
}
class B : A
{
public void Play()
{
Console.WriteLine("子类说飞起来了");
}
}
如果子类的方法是new进行声明的,父类也不能调用子类的方法,只能调用自己的方法,
class Program
{
static void
{
A a = new B();
a.Play();
Console.ReadKey();
}
}
class A
{
public virtual void Play()
{
Console.WriteLine("父类说飞起来了");
}
}
class B : A
{
public new void Play()
{
Console.WriteLine("子类说飞起来了");
}
}
★★★★★从上面的例子中可以看出,虽然父类指向了子类,但是子类没有进行override声明的话,不论父类有没有进行virtual的声明,最终调用的都是父类的方法。。。
子类用new进行方法的声明,那么子类的子类不能继承子类的方法。
class B : A
{
public new void Play()
{
Console.WriteLine("子类说飞起来了");
}
}
//class C : B {
// public override void Play()
// {
// Console.WriteLine("子类的子类说飞起来了");
// }
//}
(2.3)父类的方法用abstract进行声明,父类也必须用abstract进行声明,子类必须用override进行重写
声明为abstract的方法无法使用主体,类无法进行实例化
A a=new A();//这样写是错误的
abstract class A
{
public abstract void Play();
}
class B : A
{
public override void Play() //子类必须用override进行重写
{
Console.WriteLine("子类说飞起来了");
}
}
如果父类的方法是用private进行声明的,虽然子类也有该方法,但指向子类的父类对象,无法调用子类的方法。
class Program
{
static void
{
A a = new B();
a.Play();
a.★★★★★由于父类的方法是private的,无法调用父类(咬)这个方法,也无法调用子类(咬)这个方法
B b1 = new B();
b1.咬();
Console.ReadKey();
}
}
public class A
{
public virtual void Play()
{
Console.WriteLine("父类说飞起来了");
}
private void 咬() //父类的方法是用private 进行声明的
{
Console.WriteLine("A说了咬");
}
}
class B : A
{
public new void Play()
{
Console.WriteLine("子类说飞起来了");
}
public void 咬()
{
Console.WriteLine("B说了咬");
}
}
小结:
(1)声明的对象都是先找到本类的方法,如果子类没有该方法的话,那么继承父类的方法,否则直接调用子类自己的方法。
(2)虽然父类指向了子类,但是子类没有进行override声明的话,不论父类有没有进行virtual的声明,最终调用的都是父类的方法。。。
(3)父类的方法用abstract进行声明,父类也必须用abstract进行声明,子类必须用override进行重写,除非没有子类。
声明为abstract的方法无法使用主体,类无法进行实例化
(4)父类的方法是private的,无法调用父类(咬)这个方法,也无法调用子类(咬)这个方法,也就是说父类不能是私有的方法,否则虽然父类指向了子类,也无法调用子类的方法。
(5)子类用new进行方法的声明,那么子类的子类不能继承子类的方法,不能进行方法的重写
————————————————委托的应用——————————————————◆◆35.委托
(1).如何声明一个委托?
delegate 返回值类型 委托类型(参数)
delegate void saydelegate(string s);
(2).委托是如何指向方法的?对方法进行调用
★★★委托类型声明的委托对象指向方法
★★★委托对象调用委托的方法(看方法的参数进行调用)
委托要调用方法,肯定要有方法被委托进行调用。
可以有以下两种方法进行调用
①委托类型 委托对象 实例化委托(传参数的名称即方法的名称)
saydelegate saydelegate =new saydelegate(Say); //先要指向方法
//saydelegate("123"); //然后才能进行调用
②saydelegate saydelegate1 = Say; //如果委托的对象没有指向方法,没有进行调用,缺少一步都不行。
//saydelegate("123");
(3).在方法里调用委托
①声明一个委托 delegate bool adelegate(int i); 委托的返回值类型,参数类型要和委托调用的方法返回值类型,参数类型一致
②声明一个委托调用的方法
static bool say(int i)
{
return i > 20;
}
③在方法中进行调用委托
static List<int> list3(List<int> list1, adelegate f)
{
List<int> list2 = new List<int>();
foreach (int i in list1)
{
if (f(i))
{
list2.Add(i);
}
}
return list2;
}
④代码中调用调用委托的函数
List<int> newList =list3(list1,say);
(4).简单的在代码中声明委托
①//声明一个委托
public delegate bool adelegate(string s);
②声明委托需要执行的方法
static bool Say(string s)
{
if (s == string.Empty)
{
MessageBox.Show("请输入文本");
return false;
}
else
{
return true;
}
}
③在需要执行委托对象的声明
public adelegate adelegate1;
④在需要用委托调用方法的地方进行调用声明
adelegate1 = new adelegate(Say);
if (adelegate1(textBox1.Text.Trim()) == false)
{
textBox1.Focus();
}
(5).委托的复杂应用
第一步:声明一个委托
public delegate void delegate1(A a);
第二步:在用户控件的事件时,进行调用
private void button1_Click(object sender, EventArgs e)
{
if (adelegate != null)
{
A a = new A();
a.value = textBox1.Text.Trim();
a.isvalue = true;
adelegate(a);
if (a.isvalue == false)
{
MessageBox.Show("数据错误");
}
}
}
第三步:在窗体中进行监听,监听调用方法 在别的地方进行监听,如果听到,则把方法传给委托调用的方法。
Clientname.adelegate = Say; //把调用的方法传给委托调用的方法
第四步:声明监听的方法
void Say(A a)
{
if (a.value.Length <= 2)
{
a.isvalue = false;
}
}
(6).如何引入事件的概念
①那么存不存在清除监听和冒充监听呢?
第一步:声明委托
public delegate void adelegate();
第二步:在需要的地方执行委托
private void button1_Click(object sender, EventArgs e)
{
if (adele != null)
{
adele();
}
}
第三步:委托调用方法 控件名.委托对象
userName.adele +=new adelegate(Say);
userName.adele += new adelegate(Hellow);
第四步:声明委托调用的方法
void Say()
{
MessageBox.Show("您好啊");
}
void Hellow()
{
MessageBox.Show("hellow");
}
②清除监听
userName.adele = null;
③冒充监听
userName.adele(); //注意:如果清除监听后,则无法进行冒充监听
(7).既然是这样,那么如何防止清除和冒充监听呢,这里就引入了事件的概念。
第一种方法:
private adelegate _adele;
public event adelegate adele
{
add
{
_adele += value;
}
remove
{
_adele -= value;
}
}
进行调用的时候,内部
if (_adele != null)
{
_adele();
} 外部的话,只能通过属性进行访问。因为字段是私有的,外部无法访问。
第二种方法:其实是微软给我们封装好的
public event adelegate adele;
(8).★★★★★要看事件对应的委托是什么,方法则声明为什么。
(9)小结:
委托的返回值类型,参数类型要和方法的返回值类型,参数类型一致。
委托可以直接把一个参数传递给方法,可以有返回值,也可以没有返回值。
class Program
{
static void
{
List<int> list1 = new List<int>();
list1.Add(10);
list1.Add(20);
list1.Add(-15);
list1.Add(8);
list1.Add(-20);
adelegate adel = new adelegate(oushu);
foreach (int i in list1)
{
if (adel(i))
{
Console.WriteLine(i);
}
}
Console.ReadKey();
}
static bool oushu(int i)
{
return i > 10;
}
}
delegate bool adelegate(int i);
委托是一个类型,是把一个方法当做参数传递给另外一个方法。
class Program
{
static void
{
List<int> list1 = new List<int>();
list1.Add(10);
list1.Add(20);
list1.Add(-15);
list1.Add(8);
list1.Add(-20);
List<int> newlist = list2(list1,oushu);
foreach (int j in newlist)
{
Console.WriteLine(j);
}
Console.ReadKey();
}
static List<int> list2(List<int> list1,adelegate f)
{
List<int> list2 = new List<int>();
foreach (int i in list1)
{
if (f(i))
{
list2.Add(i);
}
}
return list2;
}
static bool oushu(int i)
{
return i > 10;
}
}
delegate bool adelegate(int i);
——————————————————————lambda表达式———————————◆◆36.lambda表达式
(1). 检索出满足条件的
int[] value = { 10, 20, 5, 1, 18, -20 };
var e1 = value.Where(i => i > 10).OrderBy(i=>i);
foreach (int j in e1)
{
Console.WriteLine(j);
}
(2).生成一个新的列表
int[] value = { 10, 20, 5, 1, 18, -20 };
var e1 = value.Select(i => i*20);
foreach (var j in e1)
{
Console.WriteLine(j);
}
(3).生成一个带【】的列表
int[] value = { 10, 20, 5, 1, 18, -20 };
var e1 = value.Where(i => i > 10).OrderBy(i => i).Select(i => "[" + i + "]");
foreach (var j in e1)
{
Console.WriteLine(j);
}
(4).运行效率更改的lambda表达式
int[] value = { 10, 20, 5, 1, 18, -20 };
var e1 = from i in value
where i > 10
orderby i descending
select i*20;
foreach (var j in e1)
{
Console.WriteLine(j);
}
————————————————正则表达式——————————————————◆◆37.正则表达式
◆◆38.扩展方法的应用
(1)扩展方法
什么样的类声明为static方法,就是没有用到任何的非static字段,属性。
static 不可以调用非static成员
(2)类上,方法上加上修饰符static.
static class StringHelper
{ public static bool IsEmail(this string s)
{
return s.Contains("@");
}
public static string Quoted(this string s)
{
return "[" + s + "]";
}
}
(3)在第一个参数上加上this,第一个参数类型要是前面用到的类型。
(4)进行调用扩展
Console.WriteLine(s.IsEmail().ToHZ()); //其实就是相当于把前面的值以指定的类型传进来了
——————————————————线程———————————————————◆◆39.线程
(1)
//不安全的线程访问控件的类型
//Label.CheckForIllegalCrossThreadCalls = false;
(2)安全的访问控件的方式。
①声明一个委托
delegate void adelegate(string s);
adelegate adelegate1 = null;
②委托调用一个方法
adelegate1 = new adelegate(SetRan);
③创建一个方法,供委托进行调用
void SetRan(string s)
{
label1.Text = s;
}
④声明线程
Thread thread1 = null;
⑤声明线程,开始线程的调用
ThreadStart ts = new ThreadStart(ran); //调用了ran这个方法
thread = new Thread(ts);
thread.Start();
⑥声明线程中要调用的方法
bool isStop = false;
void ran()
{
while (!isStop)
{
Random ran = new Random();
string s = ran.Next(10).ToString();
this.Invoke(adelegate1, s); 调用这个方法的时候去执行委托,给委托进行传参数
Thread.Sleep(200);
}
}
⑦结束线程
if (thread != null)
thread.Abort();
———————————————————接口的使用———————————————
◆◆40.接口的简单使用
(1)声明一个接口
interface Comp
{
bool Comp1(int i, int j);
}
(2)实现接口
class Incomp : Comp
{
public bool Comp1(int i, int j)
{
return i > j;
}
}
(3)委托传递方法名,接口传递实现接口的类名
static void
{
List<int> list1 = new List<int>();
list1.Add(10);
list1.Add(20);
list1.Add(30);
list1.Add(-15);
int j = list2(list1, new Incomp());//实例化一个类,把实现接口的类得对象传递过去
Console.WriteLine(j);
Console.ReadKey();
}
(4)static int list2(List<int> list1, Comp c)//接口的对象来接收实现接口的类得对象。
{
int max = list1[0];
List<int> list2 = new List<int>();
foreach (int i in list1)
{
if (c.Comp1(max,i)) 接口的对象.接口实现的方法
{
max = i;
}
}
return max;
}