1.方法和参数修饰符
参数修饰符
1.1. 参数按值传递
1.2.out 按引用传递,方法内给参数赋值。不允许,不赋值就返回。 在调用和实现该方法时,都需要out修饰符。
1.3.ref 按引用传递,方法内可选地给参数赋值。在调用和实现该方法时,都需要ref修饰符。在调用前需要对对参数初始化。
1.4.params 允许将一组可变数量的参数作为单独的逻辑参数传递。方法至多只能将最后一个参数设置为params。可以把可变数量的参数(相同类型)作为单个逻辑参数传给方法。
// params传入参数可以传入强类型数组或逗号分隔的项列表
static double CalcAverage(params double[] values)
{
double sum = 0;
if(values.Length == 0) return sum;
for(int i = 0; i < values.Length; i++)
{
sum += values[i];
}
return (sum / values.Length);
}
static void Main(string[] args)
{
...
double average = CalculateAverage(4.0, 3.0, 3.0);
double[] data = {4.0, 3.0, 3.0};
average = CalculateAverage(data);
}
1.5.可选参数【参数默认值,提供的默认值需要在编译时确定】
1.6.命名参数调用方法
static void DisplayFancy(int n1, int n2, double d1)
{
...
}
static void Main(string[] args)
{
DisplayFancy(4, 2, 0.0);
DisplayFancy(n2: 2, n1: 4, d1: 0.0);
// 混用 命名和非命名参数时,命名参数放在参数列表后面
}
【命名参数 一般 结合 可选参数一起使用。】
2.C#数组
static void SimpleArrays()
{
int[] myInts = new int[3];
}
2.1.C#数组初始化语法
static void SimpleArrays()
{
string[] stringArray = new string[]{"one", "two", "three"};
int[] intArray = new int[4]{20, 22, 23, 0};
int[] intArray = {20, 22, 23, 0};
}
2.2.隐式类型本地数组
static void SimpleArray()
{
var a = new[]{1, 2.2, 3.3};// 要求{}中每一项可以推断出一致的类型
}
2.3.object数组
static void SimpleArray()
{
object[] myObjects = new object[4];
myObjects[0] = 10;
myObjects[1] = false;
myObjects[2] = new DateTime(1969, 3, 24);
myObjects[3] = "Form&Void";
foreach(object obj in myObjects)
{
Console.WriteLine("Type:{0}, Value:{1}", obj.GetType(), obj);
}
}
2.4.多维数组
int[,] myMatrix = new int[6, 6];
static void MultiArray()
{
int[][] myMulArray = new int[5][];
for(int i = 0; i < myMulArray.Length; i++)
{
myMulArray[i] = new int[i + 7];
}
}
2.5.数组做参数和返回值
static void PrintArray(int[] myInts)
{
...
}
static string[] GetStringArray()
{
...
}
要学习的基础设施:
System.Array
3.枚举类型
enum EmpType
{
EmpTypeOne = 1,
EmpTypeTwo,
EmpTypeFive = 5,
}
3.1.指定枚举值实际存储类型【默认System.Int32】
enum EmpType : byte
{
EmpTypeOne = 1,
EmpTypeTwo,
EmpTypeFive = 5,
}
枚举属于自定义类型。可作为函数返回值,方法参数,本地变量等。
枚举变量赋值时,必须以枚举名来设置值。
要学习的基础设施:
System.Enum
static void EvaluateEnum(System.Enum e)
{
Console.WriteLine("=> Information about {0}", e.GetType().Name);
Console.WriteLine("Underlying storage type: {0}", Enum.GetUnderlyingType(e.GetType()));
Array enumData = Enum.GetValues(e.GetType());
Console.WriteLine("This enum has {0} members.", enumData.Length);
for(int i = 0; i < enumData.Length; i++)
{
Console.WriteLine("Name: {0}, Value:{0:D}", enumData.GetValue(i));
}
}
4.结构类型
结构不支持继承。
struct Point
{
public int X;
public int Y;
public void Increment()
{
X++;
Y++;
}
}
static void Main()
{
Point myPoint;// X,Y未完成初始化
myPoint.X = 100;
myPoint.Y = 200;
myPoint.Increment();
Point myPoint2 = new Point();// X,Y已经初始化
}
如果使用结构前,不为每一个公共字段数据赋值,会收到一个编译器错误。
用new创建结构变量,会调用默认构造函数。每一个字段数据采用该类型默认构造完成初始化。
5.值类型和引用类型
需要学习的.NET基础设施
System.ValueType System.Object
5.1.引用类型,值类型
(1)创建引用类型时,runtime会为其分配两个空间,一块空间分配在堆上,存储引用类型本身的数据,另一个块空间分配在栈上,存储对堆上数据的引用(实际上存储的堆上的内存地址,也就是指针)。
(2)创建值类型时, runtime会为其分配一个空间,这个空间分配在变量创建的地方,如:
如果值类型是在方法内部创建,则跟随方法入栈,分配到栈上存储。
如果值类型是引用类型的成员变量,则跟随引用类型,存储在堆上。
5.2.数据传递
1.按值传递原则
对于值类型会在栈上开辟一块新的空间,将数据复制一份新的过去,两个值类型实例对象互相独立的,对其中一个的修改不会影响到另一个;对于引用类型,也会在栈上开辟一个新的空间,将栈上的引用复制到新的空间, 但是注意,此处复制的是栈上存储的引用,也就是说栈上的两个引用类型实例对象虽然是不同的空间,但是它们存储的引用, 都是指向堆上的同一实例
2.按引用传递(Ref和Out关键字)
注:Ref和Out的区别在于Ref在传递前需要初始化
对值类型,通过加入ref和out参数后,在内存中并不是像值传递一样将栈上的数据拷贝一份到新的空间。而是在栈上新建了一个指向值类型的引用类型。引用类型指向传递值。
对引用类型,通过加入ref和out参数后,在内存中并不是像值传递一样将栈上的数据拷贝一份到新的空间。而是在栈上新建了一个指向引用类型的引用类型。引用类型指向传递引用。【引用类型内存储的值是 传递引用的地址】【传递引用内存储的值是 此引用所指向的堆中实例对象的地址】
参考:
https://www.cnblogs.com/xiaodongy/p/7989711.html
http://www.cnblogs.com/duanwg/archive/2006/07/21/456247.html
6.C#可空类型
默认下,对值类型不允许赋值为null。
对值类型类型名后加?,表示这是可空值类型。【此值类型可被赋值为null,表示类型对象没有有效值】
int? nullableint1 = 10;
int? nullableint2 = null;
// 等价与上面
Nullable<int> nullableint1 = 10;
Nullable<int> nullableint2 = null;
?后缀记法实际创建了泛型System.Nullable
// 待了解的基础设施:System.Nullable
对可空类型可使用??操作符:在可空类型值为空时,以??后的值作为表达式的值。
整体 = 可空类型 ?? 求值表达式
可空类型不为空时,整体 = 可空类型值
可空类型为空时,整体 = 求值表达式的值