目录:
- 值类型和引用类型的区别
- 装箱拆箱(强类型 弱类型)
- C# 四大基本方法:tostring() equals() gethashcode() gettype()
- 泛型(安全性 为什么效率高)
- 接口和类区别
- 抽象类(方法)和虚类(方法)的区别
- 了解下.Net framework
- Const和Readonly
- 父类与子类之间的关系 继承 重写 重载
- 单例模式 实际编程用的较多
- 序列化和反序列化
- CLR (概念 核心功能)
- Asp.net页面传值的几种方式
- Dataset和Datareader的区别
- RequireJs
- 对象获取类型
- WCF 三要素 绑定方式
- 应用程序域
- 如何处理异常
- haveing groupby
- Varchar nvarchar区别
- 常用的字符串函数(网上搜下)
- 谈谈final, finally, finalize的区别
- 了解三层架构 mvc
- ADO.NET 数据库连接常用的几个类型
C#:
值类型和引用类型的区别
值类型和引用类型理解透彻后,我们知道C#里面是值传递,但是有些变量是引用类型的,在传递和拷贝时需要特别注意。方法传递参数时加上ref(out),为引用传递参数。
值传递仅仅传递的是值,不影响原始值。
引用传递,传递的是内存地址,修改后会改变内存地址对应储存的值。
值类型:原类型(Sbyte、Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、枚举(enum)、结构(struct)。
值类型的实例一般在线程栈上分配。int a = new int();
在代表值类型实例的一个变量中,并不包含一个指向实例的指针。相反,变量包含了实例本身的字段。
由于变量已经包含了实例的字段,所以为了操作实例中的字段,不再需要提领一个指针。
值类型的实例不受垃圾回收器的控制,超出了作用范围,系统就会自动释放。
因此值类型的使用缓解了托管堆中的压力,并减少了一个应用程序在其生存期内需要进行的垃圾回收次数。
引用类型:类类型、接口类型、数组类型和委托类型。所有引用类型变量所引用的对象,其内存都是在托管堆中分配的。
使用引用类型时必须考虑性能问题,首先考虑以下事实:
- 内存必须从托管堆上分配
- 堆上分配的每个对象都有一些额外的成员(类型对象指针、同步块索引),这些成员必须初始化
- 对象中的其他字节(为字段而设)总是设为零
- 从托管堆中分配一个对象时,可能强制执行一次垃圾收集操作
SDK文档中,任何称为“类”的类型都是引用类型。例如:System.Exception类、System.IO.FileStream类以及System.Random类都是引用类型。
相反,文档中将所有值类型称为结构或枚举。例如:System.Int32结构、System.TimeSpan结构、System.DayofWeek枚举。
所有结构都是抽象类型System.ValueType的直接派生类。所有枚举都从System.Enum 抽象类型派生。System.Enum又是从System.ValueType派生。
所有值类型都是隐式密封的(sealed),目的是防止将一个值类型用作其他任何引用类型或值类型的基类型。
值类型和引用类型的区别:
- 已装箱和未装箱:值类型有两种表示形式:未装箱(unboxed)形式和已装箱(boxed)形式。||相反,引用类型总是处于已装箱形式
- 重写方法:值类型是从System.ValueType派生的。该方法提供了与System.Object定义的相同的方法。然而,System.ValueType重写了Equals方法,能在两个对象的字段完全匹配的前提下返回true。System.ValueType重写了GetHashCode方法。
- 不能有虚方法:由于不能将一个值类型作为基类型来定义一个新的值类型或者一个新的引用类型,所以不能在值类型中引入任何虚方法。所有方法都不能是抽象的,都隐式地为密封方法
- NullReferenceException异常:引用类型的变量指向的是堆上的一个对象的地址。默认情况下,在创建一个引用类型的变量时,它被初始化为null,表明引用类型的变量,当前不指向一个有效的对象。试图使用一个为null的引用类型变量,会抛出NullReferenceException异常。||值类型的变量总是包含其基础类型的一个值,而且值类型的所有成员都初始化为0 。由于值类型的变量不是指针,所有在访问一个值类型时,不可能抛出NullReferenceException异常。CLR为值类型提供了一个特殊的特性,能为值类型添加“可空性”
- 变量赋给:将一个值类型的变量赋给另一个值类型变量,会执行一次逐字段的复制(在线程堆栈上重新分配并复制成员)。||将引用类型的变量赋给另一个引用类型变量时,只复制内存地址(堆中同一个类型的实例/对象。)
- 变量操作影响:基于上一条,两个或多个引用类型的变量能引用堆中同一个对象,所有对一个变量执行的操作可能影响到另一个变量引用的对象。||相反,值类型的变量是自成一体的对象,对一个值类型变量的操作不可能影响另一个值类型变量
- 内存释放:由于未装箱的值类型不在堆上分配,所以一旦定义了该类型的一个实例的方法不再处于活动状态,为它们分配的存储就会被释放。这意味着值类型的实例在其内存被回收时,不会通过Finalize方法接收到一个通知||引用类型是在堆上分配,受垃圾回收器的控制
1.将一个值类型变量赋给另一个值类型变量时,将复制包含的值。引用类型变量的赋值只复制对对象的引用,而不复制对象本身。
2.值类型不可能派生出新的类型:所有的值类型均隐式派生自 System.ValueType。但与引用类型相同的是,结构也可以实现接口。
3.值类型不可能包含 null 值:然而,可空类型功能允许将 null 赋给值类型。
4.每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值。
装箱拆箱(强类型 弱类型)
值类型是比引用类型更“轻型”的一种类型,因为它不作为对象在托管堆中分配,不会被垃圾回收,也不通过指针来引用。
但在许多情况下,都需要获取对值类型的一个实例的引用,即将值类型转换成引用类型。
如上面代码,创建一个ArrayList对象(System.Collections命名空间中定义的一个类型)来容纳一组Point结构。
每一次循环迭代都会初始化值类型字段(x和y)。然后这个Point会存储到ArrayList中。但ArrayList中究竟存储的是什么?是Point结构,还是其他什么东西。我们必须研究ArrayList的Add方法,了解它的参数被定义成什么类型。
Add需要获取一个Object参数。换言之,Add需要获取对托管堆上的一个对象的引用(指针)来作为参数。但在之前的代码中,传递的是p,也就是一个Point,是一个值类型。为了将一个值类型转换成引用类型,要使用一个名为装箱(boxing)的机制。
对值类型的一个实例进行装箱操作时在内部发生的事情:
- 在托管堆上分配好内存。分配的内存量=值类型的各个字段需要的内存量+托管堆的所有对象都有的类型对象指针和同步块索引需要的内存量
- 值类型的字段复制到新分配的堆内存中
- 返回对象的地址,这个地址是对一个对象的引用,值类型现在是一个引用类型
C#编译器会自动生成对一个值类型的实例进行装箱所需的IL代码。
在上述代码中,C#编译器检测到 向一个需要引用类型的方法传递一个值类型,所以会自动生成代码对对象进行装箱。在运行时,当前存在于Point值类型实例p中字段会复制到新分配的Point对象中。已装箱的Point对象(现在是一个引用类型)的地址会返回给Add方法。Point对象会一直存在于堆中,直到被垃圾回收。Point值类型变量p可以重用,因为ArrayList根本不知道关于它的任何事情。在这种情况下,已装箱的值类型的生存期超过了未装箱的值类型的生存期。
在知道装箱如何进行之后,接着谈谈拆箱。
假定需要使用以下代码获取ArrayList的第一个元素:
现在是要获取ArrayList的元素0中包含的引用(或指针),并试图将其放到一个Point值类型的实例p中。包含在已装箱Point对象中的所有字段都必须复制到值类型变量p中,后者在线程栈上。
CLR分两步完成这个拆箱操作(获取地址/复制)。
- 获取已装箱的Point对象中的各个Point字段的地址。
- 将这些字段包含的值从堆中复制到基于线程栈的值类型实例中。
一个已装箱的值类型实例在拆箱时,内部可能会发生下面的异常:
- 如果包含了“对已装箱的值类型实例的引用”的变量为null,就抛出一个NullReferenceException异常
- 如果引用指向的对象不是所期待的值类型的一个已装箱实例,就抛出一个InvalidCastException异常,如下
以上代码从逻辑上说,完全可以获取o所引用一个已装箱的Int32,然后将其强制转换为一个Int16。
然而,在对一个对象进行拆箱的时候,只能将其转型为原先未装箱时的值类型---本例即为Int32,再进行转型。
下面的代码是正确的写法:
由于未装箱的值类型没有同步块索引,所以不能使用System.Threading.Monitor类型的各种方法,让多个线程同步对这个实例的访问。
C# 四大基本方法:tostring() equals() gethashcode() gettype()
System.Object的公共方法:
- Equals: 如果两个对象具有相同的值,就返回true。
- GetHashCode: 返回对象的值的一个哈希码。如果某个类型的对象要在一个哈希表集合中作为Key使用,该类型应该重写这个方法。
- ToString: 该方法默认返回类型的完整名称(this.GetType ().FullName)。然而,我们经常需要重写这个方法,使它返回一个String对象。
- GetType: 返回从Type派生的一个对象的实例,指出调用GetType的那个对象是什么类型。返回的Type对象可以和反射类配合使用,从而获取与对象的类型有关的元数据信息。GetType方法是非虚方法,public Type GetType();这样可以防止一个类重写该方法,并隐瞒其类型,从而破坏类型安全性。
System.Object的受保护的方法:
- MemberwiseClone: 这个非虚方法能创建类型的一个新实例。
- Finalize: 在垃圾回收器判断对象应该被作为垃圾回收之后,在对象的内存被实际回收之前,会调用这个虚方法。简单的说,虚方法就是可以被子类重写(override)的方法,如果子类重写了虚方法,那么运行时将使用重写后的逻辑,如果没有重写,则使用父类中虚方法的逻辑。
泛型(安全性 为什么效率高)
1.C#里的泛型是类型安全的,定义的时候使用T,D占位标示。但是实例化一个实例的时候必须制定明确的类型,不存在类型上的转换。
2.C#的泛型性能更好,为什么呢?实现类似的功能你当然可以使用Object作为基类,这样你就可以什么类型都传了。但是使用泛型,他在编译的时候使用替换占位标示的方式,相比之下不就是省去了装箱和拆箱的过程吗?
我们可能会想到使用object类,于是你写了下面这个通用的算法。这里用到的技术就是装箱和拆箱,Add方法首先将所有数据类型的数据进行装箱,这样就统一了它们的类型,然后再进行类型转换和计算,计算结果再拆箱就是要求的结果。我们知道这些频繁的装箱和拆箱操作是非常损耗性能的。另外,装箱和拆箱的代码也显得比较“难看”。
程序语言必须能够对现实中的数据进行表述,对于C#语言来讲可以使用数据类型对数据进行精确的描述。事实上这种程序语言被称作强类型语言,在这样的语言当中出现的数据都必须带有数据类型,这样的语言还有很多,比如C++、Java、Python等。与强类型语言对应的得是弱类型语言,比如VB、JavaScript等,他们没有数据类型概念。
接口和类区别
异:
- 不能直接实例化接口,
- 接口不包含方法的实现,
- 接口、类和结构可以从多个接口继承。但是C#只支持单继承:类只能从一个基类继承实现。
同:
- 接口、类和结构可以从多个接口继承,
- 接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员,
- 接口可以包含事件、索引器、方法和属性,
最关键的问题是c#基类的继承只能是子类继承于一个父类,但一个类却可以被多个接口约束(即可以实现多个接口)
抽象类(方法)和虚类(方法)的区别
http://www.cnblogs.com/blsong/archive/2010/08/12/1798064.html
什么是抽象类:
抽象类不能实例化,必须通过继承由派生类实现其抽象方法,因此对抽象类不能使用new关键字,也不能被密封。
如果派生类没有实现所有的抽象方法,则该派生类也必须声明为抽象类。另外,实现抽象方法由overriding方法来实现。
声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类。取而代之,在子类中实现该方法。
抽象类具有以下特性:
- 抽象方法是隐式的虚方法。
- 抽象类不能实例化。
- 不能用 sealed修饰符修饰抽象类,因为这两个修饰符的含义是相反的。
采用 sealed 修饰符的类无法继承,而 abstract 修饰符要求对类进行继承。
- 从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实际实现。
- 抽象类可以包含抽象方法和抽象访问器。
- 只容许在抽象类中使用抽象方法声明。
- 因为抽象方法声明不提供实际的实现,所以没有方法体。
方法声明只是以一个分号结束,并且在签名后没有大括号“{}”。
在派生类中,通过包括使用override修饰符的属性声明,可以重写抽象的继承属性。
abstract class ShapesClass
{
abstract public int Area();
}
class Square : ShapesClass
{
int side = 0;
public Square(int n)
{
side = n;
}
// Area method is required to avoid
// a compile-time error.
public override int Area()
{
return side * side;
}
static void Main()
{
Square sq = new Square(12);
Console.WriteLine("Area of the square = {0}", sq.Area());
}
}
虚方法:
1:抽象方法仅仅是声明,没有任何实现内容;抽象方法一般必须在子类中被重写以后才有使用价值
2:针对抽象方法引出的问题:如果需要在父类中提供一个方法,该方法有自己的方法体,子类根据自己的需要决定是否重写该方法,而不是必须重写,这就是 ‘虚方法’
3:使用关键字virtual修改时的虚方法
抽象方法和虚方法最重要的区别:
- 抽象方法不能实例化,要子类必须强制性的覆盖它的方法 。
而虚方法则是提供了选择,可以覆盖可以不覆盖,继承基类中的虚方法。
虚拟方法必须有一个实现部分,并为派生类提供了覆盖该方法的选项。
相反,抽象方法没有提供实现部分,强制派生类覆盖方法(否则 派生类不能成为具体类)。
- abstract方法只能在抽象类中声明,虚方法则不是。
- abstract方法必须在派生类中重写,而virtual则不必。
- abstract方法不能声明方法实体,虚方法则可以。
了解下.Net framework
.NET Framework 是以一种采用系统虚拟机运行的编程平台,以通用语言运行库(Common Language Runtime)为基础,支持多种语言(C#、VB、C++、Python等)的开发。
.NET Framework 的关键组件为公共语言运行时 (CLR) 和 .NET Framework 类库(包括ADO .NET、ASP .NET、Windows 窗体和Windows Presentation Foundation (WPF) 和 WindowsWorkflow Foundation (WF))。 .NET Framework 提供了托管执行环境、简化的开发和部署以及与各种编程语言的集成。
Const和Readonly
常量的定义,其关键字就是const。在定义常量时,必须赋予其初始值。一旦赋予了初始值后,就不能修改其值。也就是所谓的常量值不能更改的含义。
虽然说变量的值可以修改,但我们也可以定义只读的变量,方法就是在定义的时候加上关键字readonly。如下定义:
public readonly int number = 20;
变量number的值此时是只读的,不能再对其进行重新赋值的操作。在定义只读变量的时候,建议必须为变量赋予初值。如果不赋予初值,.Net会给 与警告,同时根据其类型不同,赋予不同的初值。例如int类型赋初值为0,string类型赋初值为null。由于定义的只读变量其值不可修改,因此不赋 初值的只读变量定义,没有任何意义,反而容易造成空引用对象的异常。
readonly 关键字与 const 关键字不同: const 字段只能在该字段的声明中初始化。readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly 字段可能具有不同的值。另外,const 字段是编译时常数,而 readonly 字段可用于运行时常数。
static的意义与const和readonly迥然不同。const仅用于常量定义,readonly仅用于变量定义,而static则和常量、变量无关,它是指所定义的值与类型有关,而与对象的状态无关。
父类与子类之间的关系 继承 重写 重载
- 重载(overload)指的是同一个类中有两个或多个名字相同但是参数不同(参数个数和参数类型)的方法。
- 覆盖/重写(override):继承时发生,在子类中重新定义父类中的方法,子类中的方法和父类的方法是一样的(即方法名,参数,返回值都相同),由 override 声明重写的方法称为重写基方法。通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题。子类方法的访问权限只能比父类的更大,不能更小。如果父类的方法是private类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法。
重载必须发生在一个类中,方法名相同,参数不同。值得注意的是,方法重载必须通过参数不同来判断调用哪个方法,而不能通过返回值。
重写override一般用于接口实现和继承类的方法改写。值得注意的是,被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有进行覆盖。
单例模式 实际编程用的较多
使用Singleton模式有一个必要条件:在一个系统中要求一个类只有一个实例时才应当使用单例模式。
反过来,如果一个类可以有几个实例共存,就不要使用单例模式。
注意:
不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。
不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。
public class Singleton
{
private Singleton(){}
public static readonly Singleton instance = new Singleton();
}
单例模式应用:
- 每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机。
Singleton模式包含的角色只有一个,就是Singleton。Singleton拥有一个私有构造函数,确保用户无法通过new直接实例它。
除此之外,该模式中包含一个静态私有成员变量instance与静态公有方法Instance()。Instance方法负责检验并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。
序列化和反序列化
一、概述
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
[XMLSerializer]
将此类的实例序列化成一个Xml文件.
XmlSerializer ser = new XmlSerializer(obj.GetType());
ser.Serialize(new FileStream(@"users.xml", FileMode.Create), obj);
反序列化:
XmlSerializer serializer = new XmlSerializer(Type.GetType("MyObject"));
MyObject my=(MyObject)serializer.Deserialize(new FileStream(@"users.xml",FileMode.Open));
CLR (概念 核心功能)
公共语言运行时(Common Language Runtime, CLR)是一个供多种编程语言使用的运行时。可用任何编程语言来开发代码,只要编译器是面向CLR的就行。
CLR也可以看作一个在执行时管理代码的代理,管理代码是CLR的基本原则。能够被管理的代码称为托管(managed)代码,反之称为非托管代码。
CLR的核心功能中的内存管理、程序集加载、安全性、异常处理和线程同步等可被面向CLR的所有语言使用。
Asp.net页面传值的几种方式
第一种方法:
Request.QueryString是一种非常简单的传值方式,他可以将传送的值显示在浏览器的地址栏中。如果是传递一个或多个安全性要求不高或是结构简单的数值时,可以使用这个方法。但是对于传递数组或对象的话,就不能用这个方法了。
通过URL链接地址传递 send.aspx:
代码如下:
protected void Button1_Click(object sender, EventArgs e)
{
Request.Redirect("Default2.aspx?username=honge");
}
receive.aspx:
代码如下:
string username = Request.QueryString["username"];//这样可以得到参数值。
第二种方法Session传值:
这种方法将每份数据存储于服务器变量中,可以传递比较多的数据,并且安全性较高,所以常用于用户身份的验证功能中。
通过session send.aspx:
代码如下:
protected void Button1_Click(object sender, EventArgs e)
{
Session["username"] = "honge";
Request.Redirect("Default2.aspx");
}
receive.aspx:
代码如下:
string username = Session["username"];//这样可以得到参数值。
第三种方式:Cookie传值
Cookie是一种比较特殊的数据存储方式,因为这种方式将数据存储于浏览用户的电脑中,以文本文件的形式存在于磁盘中。这种方式非常有意思,很多登录系统就是利用Cookie实现用户自动登录。
Dataset和Datareader的区别
DataReader和DataSet最大的区别在于DataReader使用时始终占用SqlConnection(俗称:非断开式连接),在线操作数据库时,任何对SqlConnection的操作都会引发DataReader的异常。因为DataReader每次只在内存中加载一条数据,所以占用的内存是很小的。由于DataReader的特殊性和高性能,所以DataReader是只进的,你读了第一条后就不能再去读取第一条了。
DataSet则是将数据一次性加载在内存中,抛弃数据库连接(俗称:断开式连接)。读取完毕即放弃数据库连接,因为DataSet将数据全部加载在内存中,所以比较消耗内存。但是确比DataReader要灵活,可以动态的添加行、列、数据,对数据库进行回传,更新操作等。
大数据量上使用 DataSet和DataReader混用,当单表数据量很大时,使用DataSet是一种很不明智的选择,因为DataSet是以DataTable内 存形式存放数据,一次性将很大数据放入内存,这是非常吃内存的。相比DataSet,DataReader就显得优雅很多,它是每次只读取一条数据,然后 循环调用机制。
Bootstrap Less
Less 是一个 CSS 预处理器,让 CSS 具有动态性。less变量,less文件。
RequireJs
RequireJS是一个JavaScript文件或者模块的加载器。它可以提高JavaScript文件的加载速度,避免不必要的堵塞。
最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
<script src="5.js"></script>
<script src="6.js"></script>
这段代码依次加载多个js文件。
这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关 系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维 护都会变得困难。
require.js的诞生,就是为了解决这两个问题:
(1)实现js文件的异步加载,避免网页失去响应;
(2)管理模块之间的依赖性,便于代码的编写和维护。
对象获取类型
C# typeof() 和 GetType()区是什么?
- typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称。
- GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法,它的作用和typeof()相同,返回Type类型的当前对象的类型。
单边继承
C#中提供了类的继承机制,但只支持单继承,而不支持多继承,即在C#中一次只允许继承一个类,不能同时继承多个类。
WCF 三要素 绑定方式
对于服务端,每个服务终结点包括三要素:地址、绑定和契约(a,b,c)。具体如下:
地址:在WCF中,每个服务都有惟一的地址。
绑定:定义服务和客户端通信的方式。
契约:属于服务公开接口的一部分,告诉其他节点这个服务是干什么的。
BasicHttpBinding
NetTcpBinding
NetPeerTcpBinding
应用程序域
因为创建进程开销很大,并且需要大量内存来虚拟化进程的地址空间。所以人们就在想可不可以在一个进程上运行多个程序呢?于是就在进程上寄宿CLR,并提出了AppDomain的概念。AppDomain是为了提供隔离而设计的。
一种边界,它由公共语言运行库围绕同一应用程序范围内创建的对象建立(即,从应用程序入口点开始,沿着对象激活的序列的任何位置)。
应用程序域有助于将在一个应用程序中创建的对象与在其他应用程序中创建的对象隔离,以使运行时行为可以预知。
在一个单独的进程中可以存在多个应用程序域。应用程序域可以理解为一种轻量级进程。起到安全的作用。占用资源小。
Hashtable和HashMap
HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,
主要区别在于HashMap允许空(null)键值(key),由于clr非线程安全,效率上可能高于Hashtable
如何处理异常
一旦 try 块中发生异常,控制流将跳转到第一个关联的异常处理程序(无论该处理程序存在于调用堆栈中的什么位置)。 在 C# 中,catch 关键字用于定义异常处理程序。
程序可以使用 throw 关键字显式地引发异常。
即使发生异常也会执行 finally 块中的代码。 使用 finally 块释放资源,例如,关闭在 try 块中打开的任何流或文件。
各种类型的异常最终都是由 System.Exception 派生而来。
最常用的Exception的属性是Message,StackTrace和InnerException。分别表示异常的文字消息,异常的方法堆栈信息,以及内部异常。
常用的字符串函数(网上搜下)
--返回字符表达式中最左侧字符的ASCII代码值
select ASCII('a')--97
select ASCII('A')--65
--将整数ASCII代码转换为字符
select CHAR(97)--a
select CHAR(65)--A
--返回字符表达式最左侧指定数目的字符串
select LEFT('abcdefg',0)--''
select LEFT('abcdefg',1)--'a'
select LEFT('abcdefg',2)--'ab'
--返回字符表达式最右侧指定数目的字符串
select RIGHT('abcdefg',0)--''
select RIGHT('abcdefg',1)--'a'
select RIGHT('abcdefg',2)--'fg'
--SUBSTRING(被截取字符串,开始位置,长度)
SELECT SUBSTRING('abcd',1,1)--a
SELECT SUBSTRING('abcd',2,2)--bc
--返回转换为小写的字符串
select LOWER('abcdefg')--'abcdefg'
--返回转换为大写的字符串
select UPPER('abcdefg')--ABCDEFG
--返回去左空格的字符串
select LTRIM(' abcdefg')--'abcdefg'
--返回去右空格的字符串
select RTRIM('abcdefg ')--'abcdefg'
--replace(被搜索字符串,要被替换的字符串,替换的字符串)
select REPLACE('abcdefg','cd','a')--abaefg
select REPLACE('abcdefg','cd','')--abefg
--返回字符串的长度
select LEN('abcdefg')--7
谈谈final, finally, finalize的区别
final修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此 一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中 不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载。
finally在异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。
finalize方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。