.NET程序执行前后各种逻辑角色之间的关系:
1、关于可空类型的理解:
首先,只能定义可空的值类型,不能定义可空的引用类型。任何可空的类型都可以表示为该类型在内存的栈中分配空间范围之内的值和null。
对于int a = null是不合法的,只能这样写:int? a = null。
定义为局部变量的可空类型必须进行初始化赋值。
方法中的可空类型参数(int? a)必须定义在必选参数之后;
其原型是System.Nullable<T>结构类型
2、关于==和equals()的比较:
在c#中,值类型的数据都是存储在内存的堆栈(简称栈)中的(注:即使是值类型,在栈中也会另外分配一块区域用于存储指向存储数据内容的内存区域的指针地址,在对值类型数据进行比较时是比较的存储数据的内容,非指针地址),引用类型指向的对象存储在内存的堆中,指向对象的引用变量存储在栈中。
基于以上:对于值类型,==和equals()是等价的,都是比较栈中的内容是否相同;对于引用类型,==比较的是堆中引用指向的对象是否相同,equals()比较的指向对象的引用变量在栈中内容是否相同;另外,string是一个比较特殊的引用类型,可以当做值类型来使用,所以对于string类型,==和equals()是等价的,都是比较其内容是否相同。
string类型初始化的特殊情况:
string a = "123"; string b = "123";
在给b初始化与a相同的内容时,并没有给b在堆中分配空间,只是把a中指向"123”的地址赋给了b,所以在栈中,a和b的内容是一样的。
3、关于IEnumerable和IEnumerator接口的理解:
MSDN解释:
IEnumerable:公开枚举数,该枚举数支持在非泛型集合上进行简单迭代。
IEnumerator:支持对非泛型集合的简单迭代。
IEnumerable接口中只有一个唯一的抽象方法GetEnumerator(),GetEnumerator()方法返回一个IEnumerator类型引用;
IEnumerator成员:
public object Current {get; set;}//表示当前迭代的子项
public bool MoveNext();//判断是否有下一项,并更新索引到下一项
public void Reset();//将索引重置为初始值
所有支持foreach语句的对象都必须继承IEnumerable接口,并实现GetEnumerator方法;
.NET Framework中定义的数组、集合类型都实现了IEnumerable接口;
自定义类型实现IEnumerable接口:
namespace ForeachTestCase { public class Car { public string Name { get; set; } public int Speed { get; set; } } public class Factory : IEnumerable { Car[] cars = new Car[2]; public Factory() { cars[0] = new Car { Name = "nisson", Speed = 120 }; cars[1] = new Car { Name = "benze", Speed = 200 }; } public IEnumerator GetEnumerator() {
//对于cars数组,framework已经定义好了GetEnumerator方法,可直接调用 return this.cars.GetEnumerator(); } } class Program { static void Main() { Factory factory = new Factory(); foreach (Car car in factory) { Console.WriteLine("car:{0}, Speed:{1}", car.Name, car.Speed); } Console.ReadKey(); IEnumerator enumerator = factory.GetEnumerator(); while (enumerator.MoveNext()) { Car car = (Car)enumerator.Current; Console.WriteLine("car:{0}, Speed:{1}", car.Name, car.Speed); } Console.ReadKey(); } } }
手动实现IEnumerator接口:
namespace ForeachTestCase1 { public class ForeachString : IEnumerable { private string[] elements; private int ctr = 0; public ForeachString(params string[] initialStrings) { elements = new string[initialStrings.Length]; foreach (string str in initialStrings) { elements[ctr++] = str; } } public ForeachString(string initialStrings, char[] splitChar) { elements = initialStrings.Split(splitChar); } public IEnumerator GetEnumerator() { return new ForeachStringEnumerator(this); } private class ForeachStringEnumerator : IEnumerator { private int position = -1; private ForeachString str; public ForeachStringEnumerator(ForeachString str) { this.str = str; } public object Current { get { return str.elements[position]; } } public bool MoveNext() { if (position < str.elements.Length - 1) { position++; return true; } else { return false; } } public void Reset() { position = -1; } } static void Main() { ForeachString str = new ForeachString("hello", "IEnumerable", "IEnumerator"); foreach (string s in str) { Console.WriteLine(s); } Console.ReadKey(); } } }
4、关于C#中方法签名中参数定义的几种关键字用法解释:
1、ref
使用ref传参时传递的是变量的引用,一般用于值类型,在方法体中对变量的更改会改变方法体之外的该变量的值,在使用ref传参时,变量必须先赋值。在定义和调用方法时,必须显式的使用ref关键字。
注意:此处所说的引用不同于引用类型的引用,此处引用是指分配在栈中的一块指针区域,指向在栈中为值类型变量对象存储分配的一块另内存区域。
public static void TestRef(ref int i) { i = 10; } static void Main() { int i = 5; TestRef(ref i); Console.WriteLine(i.ToString()); Console.ReadKey(); }
2、out
使用out传参时也是传递的参数的引用,作为out参数传递的变量不必先赋值,在方法体中必须对out参数赋值。在定义和调用方法时,必须显式的使用out关键字。
public static void TestOut(out int i) { i = 10; } static void Main() { int i; TestOut(out i); Console.WriteLine(i.ToString()); Console.ReadKey(); }
3、params
表示方法定义了不确定个数的参数,方法定义时使用数组作为参数,方法调用时传递的不确定个数的参数必须能够作为子项组成定义的数组。
public static string TestParam(params string[] paramString) { string charactors = ""; foreach (string str in paramString) { charactors += str; } return charactors; } static void Main() { Console.WriteLine(TestParam("hello", "world", "!")); Console.ReadKey(); }
4、可选参数
可选参数必须在定义时赋值,并且必须放在参数列表的最后。
可选参数定义:
public void params OptionalMethod(int a, int b=0)
可选参数在编译时确定其初始值,不能在运行时确定,如下:
//由于DateTime的Now属性是在运行时确定,所以下面的可选参数定义会产生编译时错误 public void OptionalMethod(int a, dateTime dt = DateTime.Now)
5、获取当前日期所在的那一星期的星期一和星期天的日期:
DateTime dtNow = DateTime.Now; DateTime dtMonday = dtNow.AddDays((int)(DayOfWeek.Monday - dtNow.DayOfWeek)); DateTime dtSunday = dtNow.AddDays((int)(DayOfWeek.Saturday - dtNow.DayOfWeek + 1));
6、服务器端和客服端编码/解码字符串的方法:
服务器端:
编码:HttpContext.Current.Server.HtmlEncode(stringValue)
解码:HttpContext.Current.Server.HtmlDecode(stringValue)
客服端:
编码:escape(‘stringvalue’); 解码:unescape(‘stringvalue’)
7、C#中数组声明与初始化的几种方式:
string数组声明:
string[] arr = new string[4]
string数组声明并初始化:
string[] arr = new string[2] { "cy", "kb" } string[] arr = new string[] { "cy", "kb" } string[] arr = { "cy", "kb" }
隐式数组声明:
var arr = new[] { 1, 2, 3 };
8、c#中char类型空值的几种表示方法:
char a = ‘ ’;
char a = Char.MinValue;
char a = (char)0;
char a = Convert.ToChar(0);
char a = ((char?)null).GetValueOrDefault();
9、格式化日期类型数据
HH表示为24小时制,hh表示为12小时制