C# 知识点记录(持续更新中)
从看C#入门经典开始系统的学习C#,本文主要记录学习过程中的一些知识点,也是我博客生涯的开始,比较重要成体系的部分会单重新写文章整理归纳。
1.一字不变的字符串 @字符
使转义序列不被处理,按照原样输出(除双引号外的转义序列,避免结束字符串)
string filePath = "c:\\Docs\\Source\\a.txt" string filePath = @"c:\Docs\Source\a.txt"
2.checked关键字
类型转换时,如果使用了checked发生数学运算溢出时会抛出OverflowException;如果使用了unchecked则不会检查溢出,算错了也不会报错
byte desrubaruibVar; short sourceVar = 281; desrubaruibVar = checked((byte)sourceVar);
checked还可以修饰一个语句快
int temp = int.MaxValue; try { checked { int num = temp / 20; int a = temp * 2; int c = temp * 1000; } } catch (OverflowException) { Console.WriteLine("溢出了,要处理哟"); }
vs可以设置默认开启checked的检查,在项目属性页上选择“生成”选项卡,然后点击“高级”按钮,选中“检查数学运算溢出”选项,如下示意图
3.几个字符串相关的处理函数
ToCharArray()
string类型变量可以看成char类型变量的只读数组,可以用下面的语法访问每个字符串:
string myString ="A string"; char myChar =myString[1];
但不能采用这种方式给myString的各个字符赋值,为了获得一个可写的char数组,可以使用下面的代码:
string myString ="A string"; char[] myChars =myString.ToCharArray();
接着就可以采用标准方式处理char数组了
ToLower(),ToUpper()
把字符串转换为大写或者小写形式
Trim(),去除字符串前后的空格;Trim(char[]),删除字符串中char数组包含的字符
TrimStart()、TrimEnd() 删除字符串前面、后面的空格,也可以指定char[]参数
PadLeft(length)、PadRight(length) 在字符串左边或者右边添加空格使字符串达到指定的长度,
PadLeft(length,char)、PadRight(length,char) 也可以指定char字符添加该字符
4.参数数组 params
C#允许函数指定一个(只能一个)特殊参数,这个参数必须是函数定义中的最后一个参数,参数数组允许用户使用个数不定的参数调用函数。
params类型参数主要用于在对数组长度未知(可变)的情况下,调用函数时可以传入多个不同的实参,具备很好的灵活性。
示例,
假定有一个函数GetWord(),它的第一个参数是一个string值,并返回字符串中的第一个单词
string firstWord = GetWord("This is a sentence");
结果是firstWord="This"
现在我们想添加第二个参数,可以选择返回第几个单词
string firstWord = GetWord("This is a sentence",2);
结果是firstWord="is"
现在我们还想添加第三个参数来限制返回单词的字符个数
string firstWord = GetWord("This is a sentence",4,3);
结果firstWord="sen"
定义并使用带有params类型参数的函数可以实现以上功能
public string GetWord(string str,params int[] vals) { //Action }
5.引用参数 ref 输出参数 out
ref使函数参数按引用传递,与c中使用了指针变量一样,能够直接对原数进行操作
若要使用ref参数,方法定义和调用方法都必须显式的使用ref关键字
static void ShowDouble(ref int val) { //Action } showDouble(ref myNumber);
用作ref参数的变量有两个限制:函数可能会改变引用参数的值,所以变量必须是非常量的变量;传递到ref参数的参数必须已经经过初始化,c#不允许ref参数在函数中进行初始化;
out与ref作用与用法一致,但有一些区别:
- 把未赋值的变量用作ref参数是非法的,但可以把未赋值的变量用作out参数
- 在函数使用out参数时,必须把它看成是尚未赋值的
即调用代码可以把已赋值的变量用作out参数,但存储在该变量中的值会在函数执行时丢失,在函数执行完毕后out参数的值会返回给out参数所引用的变量
6.Main函数中的string[] args
args其实是Main的一个可选参数,该参数是从应用程序的外部接受的,这些信息在运行应用程序时以命令行参数的形式指定
对于args数组中的每个参数都用空格分开,如果参数包含空格就要用双引号把参数括起来,这样才不会把这个参数解释为多个参数
在VS中我们可以在Run时添加命令行参数
右键项目-属性-调试-启动选项中输入命令行参数
7.判定函数 Debug.Assert() Trace.Assert()
这两个函数有3个参数,参数1:bool值,值为false会触发判定语句。参数2、3是两个字符串,分别把信息写到弹出的对话框中
8.IDisposable接口
MSDN中关于这个接口的说明
[ComVisible(true)] public interface IDisposable { // Methods void Dispose(); }
[ComVisible(true)]:指示该托管类型对 COM 是可见的
从这个接口可以看出,实现IDisposable接口必须实现Dispose方法
当不再需要某个对象时,可以调用Dispose方法执行一些操作,然后释放重要资源
基本应用:
定义了一个实现了IDisposable的类
public class CaryClass :IDisposable { public void DoSomething() { Console.WriteLine("Do some thing...."); } public void Dispose() { Console.WriteLine("及时释放资源"); } }
有两种方式来调用:
第一种,使用using语句,在执行完{}内容后会调用Dispose方法,然后删除对象
using (CaryClass caryClass = new CaryClass()) { caryClass.DoSomething(); }
第二种,直接调用该接口的Dispose方法
CaryClass caryClass = new CaryClass(); try { caryClass.DoSomething(); } finally { IDisposable disposable = caryClass as IDisposable; if (disposable != null) disposable.Dispose(); }
两种方式的执行结果是一样的,如下图:
9.虚方法 Virtual 抽象方法 abstract
相同点:
- 都表示此方法可以在派生类中重写
不同点:
- virtual关键字只是表明此方法可以被重写,其实它和一般的方法没有什么区别。相应的sealed关键字表示此方法不可以被重写。
- abstract没有方法体,不能有具体实现,要求派生类必须重载这个方法。
- abstract方法必须声明在抽象类中
10.抽象类、接口
抽象类和接口这两种类型用于完全不同的目的。抽象类主要用作对象系列的基类,共享某些主要特性,例如共同的目的和结构。接口则主要用于类,这些类在基础水平上有所不同,但仍然可以完成某些相同的任务。
不同点:
- 抽象类的派生类只能继承一个基类,即只能继承一个抽象类,但是可以继承多个接口
- 类中只要有一个抽象方法那么这个类就是抽象类,除此之外还可以有其他方法的实现。但是接口中不可以有方法的实现。
- 抽象类中包含字段、构造函数、析构函数、静态成员或常量等,接口中不可以。
- 抽象类中的成员可以私有的(只要不是抽象的)、受保护的、内部的或受保护的内部成员,但接口中的成员必须是公共的。
11.重写与隐藏
方法重写:就是在基类中的方法用virtual关键字来标识,然后在继承类中对该类进行重写(override),这样基类中的方法已经被重写了,已经失去了功能了。当让基类的对象的引用直接指向继承类的对象时(多态性),调用该方法则是调用的继承类的方法。
方法隐藏:无论基类中的方法是否用了virtual关键字,继承类中都可以用new关键字(如果不用new的话,不会产生错误,但会生成一个编译警告)将基类中的方法隐藏,所谓隐藏就是隐藏,不像重写,重写就是原来的(基类中)已经不存在了,而隐藏是原来的还存在。所以当让基类的对象的引用直接指向继承类的对象时(多态性),调用该方法则是调用的基类的方法。
示例:
public class BaseClass { public void functionA() { Console.WriteLine("BaseFunctionA "); } public virtual void functionB() { Console.WriteLine("BaseFunctionB "); } } public class DerivedClass:BaseClass { public new void functionA() { Console.WriteLine("DerivedFunctionA "); } public override void functionB() { Console.WriteLine("DerivedFunctionB "); } }
当利用多态性执行下面代码时:
BaseClass baseFunction=new DerivedClass(); baseFunction.functionA(); baseFunction.functionB();
结果如下:
BaseFunctionA
DerivedFunctionB
12.字段关键字readonly
表示这个字段只能在执行构造函数的过程中赋值,或由初始化赋值语句赋值
13.部分类partial
使用partial关键字可以使用部分类定义,把一个类的定义放在多个文件中。如,可将字段、属性和构造函数放在一个文件中,而把方法放在另一个文件中。
只需要在包含部分类的文件中对类使用partial关键字,如:public partial class Myclass
14.深度复制与浅度复制
深度复制与浅度复制的区别在于是否复制了子对象,即浅度复制未考虑引用类型的成员。通过浅度复制的新对象中的引用成员还会指向源对象中相同成员引用的对象,如果要创建成员的新实例(复制值,而不复制引用)就要用到深度复制。
对于浅度复制,可通过派生与System.Obect的受保护的MemberwiseClone()方法来完成,
public class Cloner { public object GetCopy() { return MembeerwiseClone(); } } Cloner Source=new Cloner(); Cloner Target=(Source)Source.GetClone();
对于深度复制,.NET Framework提供了ICloneable 接口,包含一个成员 Clone方法,它用于支持除 MemberwiseClone 所提供的克隆之外的克隆
示例:
public class Content { public int Val; } public class Cloner:ICloneable { public Content MyContent =new Content(); public Cloner(int newVal) { MyContent.Val=newVal; } public object Clone() { Cloner clonedCloner = new Cloner(MyContent.Val); return clonedCloner; } }
基于.Net的反射机制,也可以用有一个通用的序列化方法,先序列化,然后再反序列化回来
public object Clone() { Cloner clonedCloner = new Cloner (); XmlStorageHelper helper = new XmlStorageHelper(); string strXml = helper.ConvertToString(this); helper.LoadFromString(clonedCloner, strXml); //从XML字符串来赋值 return clonedCloner ; }
15.封箱和拆箱
封箱boxing是把值类型转或为System.Object类型,或者转化为由值类型实现的接口类型
拆箱unboxing是相反的转换过程
示例:
interface MyInterface { } struct MyStruct : MyInterface { public int Val; } //把结构封箱到接口中 MyStruct valType1 =new MySruct(); MyInterface ValType =vakType1; //拆箱 MyStruct ValType2 =(MyStruct)reType;
从示例中可以看出,封箱是在没有用户干涉的情况下进行的,但拆箱一个值需要进行显示转换(封箱是隐式的)
16. is运算符 as运算符
is运算符用来检查对象是不是给定的类型,或者是否可以转换为给定的类型,如果是就返回true
as运算符把一种类型转换为指定的引用类型,若不不能转换则表达式的结果是null
17.运算符重载
语法:public static type operator op (参数列表)
public static:运算符重载方法的访问修饰符必须是public,该方法必须是静态方法
type:返回值数据类型
perator:运算符重载的关键字
op:表示要重载的运算符
转换运算符也可以重载 implicit(隐式) explicit(显式)
18. 比较对象 IComparable和IComparer接口
IComparable和IComparer接口是.Net比较对象的标准方式,他们的区别是
- IComparable在要比较的对象的类中实现,可以比较该对象和另一个对象
- IComparer在一个单独的类中实现,可以比较任意两个对象
一般在IComparable给出类的默认比较代码,使用其他类给出非默认的比较代码
Comparer类是IComparer接口的默认实现,执行区分大小写的比较。CaseInsensitiveComparer 类是 IComparer 接口的实现,该接口执行不区分大小写的字符串比较。
19.定制异常
在System命名空间中有两个基本的异常类ApplicationException和SystemException,它们派生于Exception。SystemException用作.NET Framework预定义的异常的基类,ApplicationException由开发人员用于派生自己的异常类。但最近的最佳做法是从Exception派生异常,ApplicationException类在未来可能会被废弃
20.匿名类型
var curry = new { MainIngredient="Lamb", Style = "Dhansal" };
这个类什么也不做,知识存储结构化数据。 其中的属性为只读属性,表示要在数据存储对象中修改属性的值,就不能使用匿名类型
21.动态类型Dynamic
动态类型不同寻常之处是它尽在编译期间存在,在运行期间它会被Object类型替代。
22.高级方法参数 可选参数 命名参数
可选参数:可以为参数设置一个默认值,如果调用此方法时没有为该参数提供直,就使用默认值
public List<string< GetWords( string sentence, bool capitalizeWords = false) { ... }
对于可选参数capializeWords的默认值存在一些限制。默认值必须是字面值、常量值。
还可以用Optional特性定义可选参数,使用这种语法就无法为参数提供默认值
可使用可选值时,他们必须放在参数列表末尾
使用可选参数时,如果有多个可选参数,你只想给其中某个或某几个参数赋值时,就需要用到命名参数。它是在方法调用时指定传递的参数名
MyMethod(optionalParameter1:value1,optionalParameter2:value2)
23.GUID
全局唯一标识符,在 Windows 平台上,GUID 广泛应用于微软的产品中,GUID 的格式为“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”
using System; namespace GUID测试 { class Program { static void Main(string[] args) { //产生一个新的GUID并输出 Console.WriteLine(System.Guid.NewGuid()); Console.ReadKey(); } } }
WPF
1.附加属性
附加属性(Attached Property)是一种在定义该属性的类的实例的每个子对象上都可用的属性。比如Grid控件定义了Column、Row这两个属性,Grid控件的每个子控件都可以使用Column、Row属性来指定自己属于Grid控件网格中的位置。
2.Border控件
Border控件只能包含一个子控件,该子控件会完全充满整个Border,可使用Margin与Padding来设置Border在容器中的位置及Border中内容相对Border本身的位置。