lastpoem / c-sharp Goto Github PK
View Code? Open in Web Editor NEWC#学习
C#学习
参数数组允许向方法传递数量不定的自变量。参数数组使用params修饰符进行声明。参数数组只能是方法的最后一个参数,且参数数组类型必须是一维数组。 System.Console 类的 Write 和 WriteLine 方法是参数数组用法的典型示例
public class Console
{
public static void Write(string fmt, params object[] args) { }
public static void WriteLine(string fmt, params object[] args) { }
// ...
}
在使用参数数组的方法中,参数数组的行为与数组类型的常规参数完全相同。 不过,在调用包含形参数组的方法时,要么可以传递形参数组类型的一个实参,要么可以传递形参数组的元素类型的任意数量实参。 在后一种情况中,数组实例会自动创建,并初始化为包含给定的自变量。 以下示例:
int x, y, z;
x = 3;
y = 4;
z = 5;
Console.WriteLine("x={0} y={1} z={2}", x, y, z);
等同于
int x = 3, y = 4, z = 5;
string s = "x={0} y={1} z={2}";
object[] args = new object[3];
args[0] = x;
args[1] = y;
args[2] = z;
Console.WriteLine(s, args);
C#中的委托类似于C或C++中函数指针。委托是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。
委托特别用于实现事件和回调方法。所有委托都派生于System.Delegate类。
声明委托:
public delegate int MyDelegate(string s);
delegate
实例化委托:
一旦声明了委托类型,委托对象必须使用new关键字来创建,且与一个特定的方法有关。当创建委托时,传递到new语句的参数就像方法调用一样书写,但不带参数。例如:
public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
示例:
using System;
delegate int NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
// 创建委托实例
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
// 使用委托对象调用方法
nc1(25);
Console.WriteLine("Value of Num: {0}", getNum());
nc2(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
.NET是微软开发的一个平台,而C#是为宣传.NET而创立的一种语言,直接集成在VS .NET中。C#和.NET关系非常密切。
.NET有两个重要特性:跨语言和跨平台。
跨语言:只要是面向.NET平台的编程语言(C#, C++, VB, F#等),用其中一种语言编写的类可以无缝在另一种语言编写的程序中实现互操作。
跨平台:一次编译,不需要任何代码修改,应用程序可以在任何有.NET框架实现的平台上,代码不依赖操作系统和硬件环境,而是.NET框架。
CLS,Command Language Specification,公共语言规范。 .NET的跨语言是通过CLS实现的。这是一组语言互操作的标准规范,从类型,命名,事件,属性,数组等方面对语言进行了共性定义和规范,这些东西被提交给ECMA,称为共同语言基础设施。
如果要编写跨语言组件,就必须遵循这些规范。
就类型而言,CLS定义了C#语言中符合规范的类型(如Byte, Int16, Int32等)和不符合的(如sByte, UInt16, Uint32等)。
为方便开发者开发,.NET提供一个特性叫CLSCompliantAttribute,代码被这个标记后如果写的代码不符合CLS规范,就会被编译器警告。
CLS规则是面向那些公开可被其他程序集访问的成员,如public,protected,对于程序集内部成员如Private,internal不会执行该检测规则。
当需要设计面向.NET语言时所需要遵循一个体系(.NET平台下的语言都支持的一个体系),这个体系就是
显然,CLS是CTS的子集。
如果一个语言支持CTS,它就面向.NET平台的语言。
微软将CTS和.NET的其他一些组件提交给ECMA成为公开标准,最后形成的标准称为**CLI(Common Language Infrastructure)公共语言基础结构。**从规范方面来说,CLI可和JVM对标。
CTS中有一条是要求基元数据类型的类库。类库是类的逻辑集合,开发中用过或写过的工具类,比如搞web的经常用到JsonHelper, XmlHelper, HttpHelper等。这些类通常会在命名为Tool,Utility等这样的项目中。这些类的集合在逻辑上称为类库。
基础类库BCL
当用VS创建一个项目后,项目已经引用好了通过.NET下的语言编写好的一些类库,比如控制台中直接可以用Console类输出信息,或using System.IO来通过File类对文件进行读取或写入操作。这些在开发中使用的提供基本功能的类,称为BCL(Base Class Library),基础类库,大多包含在System命名空间下。基础类库包含基本数据类型,文件操作,集合,自定义属性,格式设置,安全属性,I/O流,字符串操作,事件日志等类型。
框架类库FCL
由微软开发的类库统称为框架类库FCL(Framework Class Library)。BCL属性FCL的一个基础部分。FCL大部分类是通过C#编写的。
每门语言会定义一些基本类型,如C#通过int定义整型,string定义字符串,object定义根类。描述一个类型对象时可以有两种写法:
string a = "1";
object c = "1";
String b = "1";
Object d = "1";
上边小写的表示关键字,大写的表示类型。无论用什么语言,只要引用了FCL,都可以通过new一个类的方式表达数据类型,如new String('1', 1);但这样太繁琐,C#就用int关键字表示System.Int32。这些被编译器直接支持的类型叫基元类型,被直接映射于BCL中具体的类。
CTS中一个非常重要的规则,类与类之间只能单继承,System.Object是所有类型的根,任何类都显式或隐式继承System.Object
System.Object定义了类型的最基本的行为:用于实例比较的Equals系列方法、用于Hash表中Hash码的GetHashCode、用于Clr运行时获取的类型信息GetType、用于表示当前对象字符串的ToString、用于执行实例的浅复制MemberwiseClone、用于GC回收前操作的析构方法Finalize 这6类方法。
Object不仅是C#语义的类型根,还是整个FCL的类型根。
CTS定义了单继承,但C++例外。C++/CLI作为C++在.NET中的实现,如果试图在托管代码中多继承就会报错。
早期,人们直接输入010100这样的没有语义的二进制指令让计算机工作,可读性几乎无。后来就出现了吧编程语言。计算机最终只识别二进制指令,那么编程语言写出的代码就必须转换为供机器识别的指令。
从一门编程语言编写的代码文件转为能让本级识别的指令,需要翻译。现在的计算机是运行操作系统的,光翻译成机器指令也不行,还需要让代码文件转为可供操作系统执行的程序。这些步骤就是编译环节。翻译过程需要编译器来完成。
不同厂商CPU有不同指令集,为了克服面向CPU的指令集的难读,难编难记和易出错的缺点,后来出现了面向特定CPU的特定汇编语言,比如我打上这样的x86汇编指令 mov ax,bx ,然后用上用机器码做的汇编器,它将会被翻译成 1000100111011000 这样的二进制01格式的机器指令.
不同CPU架构上的汇编语言指令不同,为了统一一套写法,同时又不失汇编的表达能力,C语言就诞生了。C语言写的代码文件,会被C编译器转换成对应平台的汇编指令,再转为机器码,最后将这些过程中产生的中间模块链接成一个可被操作系统执行的程序。
将更偏机器实现的汇编称为低级语言,与汇编相比,C语言称为高级语言。
C#在编码时不需要偏向特定平台的实现,翻译过程也基本遵循这个过程。它的编译模型和C语言类似,都属于间接转换的中间步骤,所以可以跨平台。
C#相比C更为高级。几乎不需要使用指针,意味着几乎不用人为内存管控和安全考虑因素。
如果说C#一段代码可以完成其它低级语言一大段任务,那么我们可以说它特性丰富或者类库丰富。而用C#编程不需要人为内存管控是怎么做到的呢?
.NET提供了一个垃圾回收器(GC)来完成这部分工作,创建类型的时候,它会自动分配所需要的这部分内存空间。相当于,有一个专门的软件或进程,它会读取你的代码,然后执行这行代码的时候,它做了内存分配工作。 这部分本该你做的工作,它帮你做了,这就是“托管”的概念。比如现实中 托管店铺、托管教育等这样的别人替你完成的概念。
因此,C#被称之为托管语言。C#编写的代码也就称之为托管代码,C#生成的模块称之为托管模块等。(对于托管的资源,是不需要也无法我们人工去干预的,但我们可以了解它的一些机制原理,在后文我会简单介绍。)
只要有比较,就会产生概念。那么在C#角度,那些脱离了.NET提供的诸如垃圾回收器这样的环境管制,就是对应的 非托管了。
编写程序中有点模块是由托管代码编写,有点调用了非托管代码。在.NET Framework中也有一套基于此操作系统SEH的异常机制,理想的机制设定下我们可以直接通过catch(e)或catch来捕获指定的异常和框架设计人员允许我们捕获的异常。
.NET 不仅提供了自动内存管理的支持,还提供了一些如类型安全,应用程序域,异常机制等支持,这些统称为CLR(Common Language Runtime)公共语言运行时库。
CLR是.NET类型系统的基础,所有.NET技术建立在此之上。执行托管代码前,总会先运行这些运行库代码,通过运行库的代码调用,构成了一个用来支持托管程序的运行环境,进而完成不需要开发人员手动管理内存,一套代码可在各大平台跑的操作。
这套环境及体系的完善,以至于像一个小型系统,所以通常称CLR为.NET虚拟机。托管程序实际上是寄宿于.NET虚拟机中。
容纳.NET虚拟机的进程是CLR的宿主进程,该程序称为运行时主机。这些运行库的代码,全是由C/C++编写,具体表现为以mscoree.dll为代表的核心dll文件,该dll提供了N多函数用来构建一个CLR环境 ,最后当运行时环境构建完毕(一些函数执行完毕)后,调用_CorDllMain或_CorExeMain来查找并执行托管程序的入口方法(如控制台就是Main方法)。如果你足够熟悉CLR,那么你完全可以在一个非托管程序中通过调用运行库函数来定制CLR并执行托管代码。像SqlServer就集成了CLR,可以使用任何 .NET Framework 语言编写存储过程、触发器、用户定义类型、用户定义函数(标量函数和表值函数)以及用户定义的聚合函数。
C#编写的程序如果想运行就必须依靠.NET提供的CLR环境支持。CLR是.NET技术框架的一部分,只要在Windows系统中安装.NET Framework即可。
Windows系统默认安装.NET Framework,并且可以安装多个版本。某些应用程序可能依赖于特定版本,因此一般不用卸载。
自.NET Framework 4以来的所有.NET Framework版本都是直接在v4.0.30319文件夹上覆盖更新,并且无法安装以前的4.x系列的老版本,所以v4.0.30319这个目录中其实放的是你最后一次更新的NET Framework版本。
编译器,将源代码文件翻译成计算机可识别的二级制程序。在.NET Framework目录中附带用于C#语义的命令行形式的编译器csc.exe和用于VB语言的vbc.exe
用编译器可以将后缀.cs(C#)和.vb(VB) 类型文件编译成程序集。
程序集是一个抽象概念,不同编译选项会产生不同形式的程序集。以文件个数区分,可以分为单文件程序集和多文件程序集。
无论单文件程序集还是多文件程序集,总有一个核心文件,就是表现为后缀.dll和.exe格式的文件。它们都是标准的PE格式的文件,主要由4部分组成:
一、PE头,即Windows系统上的可移植可执行文件的标准格式
二、CLR头,它是托管模块特有的,主要包括
- 用于记录在源代码中所定义类型的定义表,
- 引用其它程序集中的类型成员的引用表
- 用于描述杂项(如版本,发行者,语言文化,多文件程序集中的一些资源文件等)的清单表
三、IL代码(也称MSIL,后来改名为CIL: Common Intermediate Language通用中间语言),是介于源代码和本机机器指令中间的代码,将通过CLR在不同平台产生不同的二级制机器码。
四、一些资源文件
多文件程序集的诞生场景有:比如我想为.exe绑定资源文件(如Icon图标),或者我想按照功能以增量的方式来按需编译成.dll文件。 通常很少情况下才会将源代码编译成多文件程序集,并且在VS IDE中总是将源代码给编译成单文件的程序集(要么是.dll或.exe)
字符串类型是引用类型。是System.String类的别名。它是从对象类型派生的。字符串类型的值可以通过两种形式进行分配: 引号和@引号。在字符串前引号前加@ 表示将转义字符\当做普通字符对待。
如:String str = "come with me";
String netstr = @"C:\Windows";
等价于
String netstr = "C:\Windows";
数组是一个存储相同类型元素的固定大小的顺序集合。
所有的数组都是由连续的内存位置组成的,最低的地址对应第一个元素,最高的地址对应最后一个元素。由于内存连续,数组效率较高。
声明数组
datatype[] arrayName;
其中,datatype是指定被存储元素的类型;
[]指定数组的秩(维度)。指定数组的大小。
arrayName指定数组名称。
如 double[] balance;
初始化数组
声明一个数组不会在内存中初始化数组。
数组是引用类型,因此需要用new关键字来创建数组实例。
double[] balance = new double[10];
或者
double[] balance = {2, 3, 4, 4, 5};
数组长度不可改变。因此必须在初始化的时候就要确定数组长度(可以在[]里显式确定数组长度,也可以在初始化的时候传入一定数量的元素
C#支持实例和静态构造函数。实例构造函数是实现初始化类实例所需执行的操作的成员。静态构造函数是实现首次加载类时初始化类本身所需执行的操作的成员。
构造函数声明方式和方法一样,都没有返回类型,且与类同名。带static修饰符的则说明是静态构造器。
构造器可以重载。和其它成员不同,实例构造器不能被继承。类中只能包含实际上已在该类中声明的实例构造函数。不写实例构造函数的话,类会自动生成一个无参默认构造函数。
属性是字段的自然扩展。两者都是有类型有名称的成员,访问语法也相同。然而和字段不同的是,属性不指名存储位置。相反,属性包含访问器,用于指定在读取或写入属性值时执行的语句。
属性的声明方式和字段相似,但结尾不是分号,而是用{}括起来的访问器和设置器。同时包含set和get的属性是读写属性,只有get的属性是只读属性,只有set的属性是只写属性。
get 访问器对应于包含属性类型的返回值的无参数方法。 set 访问器对应于包含一个名为 value 的参数但不含返回类型的方法。 get 访问器会计算属性的值。 set 访问器会为属性提供新值。 当属性是赋值的目标,或者是 ++ 或 -- 的操作数时,会调用 set 访问器。 在引用了属性的其他情况下,会调用 get 访问器。
像字段和方法,C#同时提供了实例属性和静态属性。静态属性使用静态修饰符进行声明,而实例属性不使用静态修饰符进行声明。属性的访问器可以是虚的。如果属性声明包含virtual,abstract或override修饰符,则适用于属性的访问器。
索引器是可以将对象编入索引,像处理数组一样。索引器的声明类似于属性,但是索引器名称格式是this后跟着被[]括起来的参数列表。参数可以被索引器访问器访问。类似于属性,索引器可以被读写,只读,只写。索引访问器也可以是虚的。
索引可以被重载,一个类可声明多个索引器,只要参数数量或类型不同即可。
借助事件成员,类或对象可以提供通知。事件声明方式与字段类似,区别是事件声明包括event关键字,且类型必须是委托类型。
在声明事件成员类中,事件行为与委托类型的字段完全相同(前提是事件不是抽象的,且不声明访问器)。字段存储对委托的引用,委托表示已添加到事件的事件处理程序。如果没有任何事件处理程序,则字段为null。
示例代码:
// 泛型类
public class MyList<T>
{
const int DefaultCapacity = 4;
T[] _items;
int _count;
// 构造器,参数带默认值
public MyList(int capacity = DefaultCapacity)
{
_items = new T[capacity];
}
public int Count => _count;
public int Capacity
{
get => _items.Length;
set
{
if (value < _count) value = _count;
if (value != _items.Length)
{
T[] newItems = new T[value];
Array.Copy(_items, 0, newItems, 0, _count);
_items = newItems;
}
}
}
public T this[int index]
{
get => _items[index];
set
{
_items[index] = value;
OnChanged();
}
}
public void Add(T item)
{
if (_count == Capacity) Capacity = _count * 2;
_items[_count] = item;
_count++;
OnChanged();
}
protected virtual void OnChanged() =>
Changed?.Invoke(this, EventArgs.Empty);
public override bool Equals(object other) =>
Equals(this, other as MyList<T>);
static bool Equals(MyList<T> a, MyList<T> b)
{
if (Object.ReferenceEquals(a, null)) return Object.ReferenceEquals(b, null);
if (Object.ReferenceEquals(b, null) || a._count != b._count)
return false;
for (int i = 0; i < a._count; i++)
{
if (!object.Equals(a._items[i], b._items[i]))
{
return false;
}
}
return true;
}
public event EventHandler Changed;
public static bool operator ==(MyList<T> a, MyList<T> b) =>
Equals(a, b);
public static bool operator !=(MyList<T> a, MyList<T> b) =>
!Equals(a, b);
}
封装是为了防止对实现细节的访问。
主要通过访问修饰符设置使用者的访问权限来实现。
访问修饰符定义了一个类成员的范围和可见性
public: 所有对象都可以访问;
private: 对象本身在对象内部可以访问;
protected: 只有该类对象及其子类对象可以访问;
internal: 同一个程序集对象可以访问;
protected internal: 访问限于当前程序集或派生自包含类的类型。
使用static修饰符声明的方法是静态方法。静态方法不对特定实例起作用,只能直接访问静态成员。
未使用static修饰符声明的方法是实例方法。实例方法对特点的实例起作用,并能访问静态和实例成员。其中调用实例方法的实例可以作为this显式访问(说人话,实例方法的this是调用这个方法的实例)。在静态方法中使用this会出错。
如果实例方法声明中有virtual修饰符,可以将实例方法称为虚方法。如果没有,则实例方法为非虚方法。
注:虚方法首先是实例方法。
对于虚方法来说,调用哪个方法取决于该实例运行时的类型。
对于非虚方法来说,调用哪个方法取决于该实例编译时的类型。
可以在派生类中重写虚方法。如果实例方法声明中有override修饰符,那么实例方法可以重写签名相同的继承虚方法。虚方法声明引入了新方法。重写方法声明通过提供现有继承的虚方法的新实现,专门针对该方法。
抽象方法是没有实现代码的虚方法。抽象方法使用abstract修饰符声明,仅可在抽象类中使用。必须在所有非抽象派生类中重写抽象方法。
注:抽象方法首先是虚方法。
// 声明了名为表达式的抽象类
public abstract class Expression
{
// 声明了名为Evaluate的抽象方法,返回值为double,
public abstract double Evaluate(Dictionary<string, object> vars);
}
// 声明了名为Constant的类,继承了Expression
public class Constant : Expression
{
// 成员变量
double _value;
// 构造方法
public Constant(double value)
{
_value = value;
}
// 重写继承的抽象方法Evaluate
public override double Evaluate(Dictionary <string, object> vars)
{
return _value;
}
}
// 声明了名为VariableReference的类,继承Expression
public class Variablereference : Expression
{
string _name;
public VariableReference(string name)
{
_name = name;
}
// 重写继承的抽象方法Evaluate
public override double Evaluate(Dictionary <string, object> vars)
{
object value = vars[_name] ?? throw new Exception($"Unknown variable: {_name}");
return Convert.ToDouble(value);
}
}
声明了一个名为Operation的类,继承了Expression
public class Operation : Expression
{
Expression _left;
char _op;
Expression _right;
public Operation(Expression left, char op, Expression right)
{
_left = left;
_op = op;
_right = right;
}
public override double Evaluate(Dictionary <string, object> vars)
{
double x = _left.Evaluate(vars);
double y = _right.Evaluate(vars);
seitch(_op)
{
case '+': return x + y;
case '-': return x - y;
case '*': return x * y;
case '/': return x / y;
default: throw new Exception("Unknown operator");
}
}
}
Expression e = new Operation(
new VariableReference("x"),
'+',
new Constant(3));
Expression e = new Operation(
new VariableReference("x"),
'*',
new Operation(
new VariableReference("y"),
'+',
new Constant(2)
)
);
Dictionary<string, object> vars = new Dictionary<string, object>();
vars["x"] = 3;
vars["y"] = 5;
Console.WriteLine(e.Evaluate(vars)); // "21"
vars["x"] = 1.5;
vars["y"] = 9;
Console.WriteLine(e.Evaluate(vars)); // "16.5"
方法重载允许在同一个类中有多个同名方法,只要它们有唯一签名(参数?)。当编译重载方法的调用时,编译器会使用重载决策决定使用哪个特定方法。重载决策找到参数最匹配的方法使用,如果找不到就会报错。
class OverloadingExample
{
static void F() => Console.WriteLine("F()");
static void F(object x) => Console.WriteLine("F(object)");
static void F(int x) => Console.WriteLine("F(int)");
static void F(double x) => Console.WriteLine("F(double)");
static void F<T>(T x) => Console.WriteLine("F<T>(T)");
static void F(double x, double y) => Console.WriteLine("F(double, double)");
public static void UsageExample()
{
F(); // Invokes F()
F(1); // Invokes F(int)
F(1.0); // Invokes F(double)
F("abc"); // Invokes F<string>(string)
F((double)1); // Invokes F(double)
F((object)1); // Invokes F(object)
F<int>(1); // Invokes F<int>(int)
F(1, 1); // Invokes F(double, double)
}
关于编译时类型与运行时类型:
class A
{...}
class B
{...}
class App
{
public static void Main()
{
A a = new A(); //实例化一个A的对象a
B b = a; //把B的对象b指向a
}
}
代码里的对象a没什么好说的,运行时与编译时类型均为A
对象b的运行时类型为A,编译时类型为B。
原因:对象a, b本身都是引用类型,在编译时对象b的类型由代码 B b确定。但在运行代码时发现b引用的内容是a所引用的内容,a则是A的实例对象。所以b的运行时类型就和a的一样了。
C#中有一种类型是动态类型Dynamic,和 对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型的类型检查是运行时检查的。
中间件是处理http请求或响应的管道
Logging - StaticFiles - MVC
当用户发起http请求的时候,
第一个中间件Logging 日志记录时间地址信息
第二个StaticFiles存放各种静态文件,js等
第三个是到MVC中间件,拦截所有请求,处理响应等,然后放到管道中返回。
特点:
中间件可同时被访问和请求(可以同时处理传入的请求和传出的响应)
可以处理请求后,然后将请求传递给下一个中间件
可以处理请求后,让管道短路
可以处理传出响应
中间件是按照添加的顺序执行的
C#是一种简洁的面向对象语言,类型安全。
平台
C#程序在.NET上运行,.NET是名为公共语言运行时(CLR)的虚执行系统和一组同一类库。CLR是微软对公共语言基础结构CLI 国际标准的商业实现。CLI是创建执行和开发环境的基础,语言和库可以在其中无缝协同工作。
运行
C#编写的源代码被编译成符合CLI规范的中间语言IL。IL代码和资源(如位图和字符串)存储在程序集中,扩展名通常为.dll。程序集包含一个介绍程序集的类型,版本和区域性的清单。
执行C#程序时,程序集将加载到CLR。CLR会直接执行实时JIT编译,将IL代码转换成本机指令。CLR可提供其他与自动垃圾回收,异常处理和资源管理相关的服务。CLR执行的代码有时称为托管代码,被编译成面向特定系统的本级语言。
语言的互操性是.NET的一项重要功能。C#编译器生成的IL代码符合公共类型规范CTS。通过C#生成的IL代码可以通过.NET版本的F#, VB, C++或其它二十多种与CTS兼容的任何语言生成的代码进行交互。
功能特性
垃圾回收会自动回收无法访问的未使用对象所占用的内存。
异常处理提供一种结构化可扩展的方法进行错误检测和恢复。
Lambda表达式支持函数编程技术。
查询语法创建一个公共模式,用于处理来自任何源的数据。
异步操作支持提供用于构建分布式系统的语法。
模式匹配提供语法,将数据从新式分布式系统中的算法中分离出来。
C#采用统一的类型系统,所有类型都继承自一个根object类型。所有类型共用一组通用运算。任何类型的值都可以一致进行存储,传输和处理。还支持用户定义的引用类型和值类型。允许动态分配轻型结构的对象和内嵌存储。
Hello World
using System;
class Hello
{
static void Main()
{
Console.WriteLine("Hello, World");
}
}
using用来引用命名空间。程序中的Console类就是System中的成员类,由于使用了using System,程序可以
用Console.WriteLine作为System.Console.WriteLine的简写。
程序声明的Hello类里只有一个成员,即Main方法。Main方法使用static修饰符进行声明。实例方法可使用关键字this引用特定封闭对象实例,而静态方法则可以在不引用特定对象的情况下运行。按约定,Main静态方法是C#程序入口点。
类型和变量
C#有两种类型:值类型和引用类型。值类型变量直接包含数据,引用类型变量则存储对数据的引用。
C# 值类型又细分为简单类型、枚举类型、结构类型、可以为 null 的值类型和元组值类型。 C# 引用类型又细分为类类型、接口类型、数组类型和委托类型。
一、值类型:
1.简单类型
有符号整型:sbyte, short, int, long
无符号整型:byte,ushort, uint, ulong
Unicode字符:char,表示UTF-16代码单元
IEEE二进制浮点:float,double
高精度十进制浮点:decimal
布尔:bool
类,结构体,枚举,接口,委托
所有数据类型由基类Object派生而来,Object派生出引用类型和值类型,
其中引用类型包括类,接口和委托,值类型包括结构体和枚举。
一个C#类型中所包含的信息
API相关功能
可以做的事:
主要接口:
ICreator接口
主要创建对象,图层和几何体
Feature Layers 加载矢量图层和新建矢量图层
Raster Layers 加载影像和高程栅格图层
Mesh Layers 加载一个本地文件或TerraGate SFS服务。流方式优化的3D Mesh图层数据集
Presentations 创建新的演示
Locations 创建位置对象和定位
2D Objects处理文本和图像标签,二维图元(文本标签,图像标签,线,面,矩形,规则多边形,二维箭头,圆,椭圆,弧和平铺于地表的视频等)
3D Objects 处理三维模型和图元(三维模型,点云模型,建筑物,3D多边形,正方体,球,3D箭头灯)。
Messages 创建绑定到其它对象的消息框和工程树上的消息框。
每个TE对象和工程树组都会被分配一个字符串,这个字符串被称为客户端数据,被保存在Fly文件中。客户端数据保存在某个特定组或对象的信息或是应用程序相关信息。为了避免TE和不同客户端应用程序冲突,客户端数据将配置在命名空间中。每个命名空间都被定义为一个字符串标识符。你需要定义命名空间并利用它保存和检索数据。
对于对象,任何继承自ITerraExplorerObject71都有一个ClientData属性可供读写。
特征层属于一个复杂的TE对象,显示来自不同数据源的特征数据。
值类型包括基本数据类型(整型,长整形,浮点型,字符型,布尔型),枚举型(枚举)和结构类型(结构)
引用类型包括类(基类 System.Object,字符串,自定义类),接口,以及数组。
1.值类型与引用类型存储方式:
引用类型: 存储在堆中,类型实例化时,会在堆中开辟一部分空间存储类的实例。但类对象的引用还是存储在栈中。
值类型:值类型总是分配在它声明的地方,作为局部变量时,存储在栈中,作为类对象的字段时,跟随此类存储在堆中。
class Program
{
static void Main(string[] args)
{\
// s :对象的引用,存储在栈中
// new Student() :对象,存储在堆中
Student s = new Student();
// a :局部变量,存在栈中
int a = 0;
}
}
internal class Student
{
// 类的字段,跟随此类存在堆中
public int age = 0;
}
2.值类型和引用类型区别:
引用类型和值类型都继承自System.Object类。不同之处:几乎所有引用类型都直接从System.Object类继承,而值类型是从System.Object类的子类System.ValueType继承。
在给引用类型变量赋值的时候,其实只是赋值了对象的引用,而给值类型变量赋值的时候是创建了一个副本
class Program
{
static void Main(String[] args)
{
// 引用类型赋值
Student s = new Student();
s.Age = 10;
Student s2 = s; // 给引用类型变量赋值时只是赋值了对象的引用
s2.Age = 20;
Console.WriteLine(s.Age); // 输出20
// 值类型 赋值
int a = 0;
int a2 = a;
a2 = 10;
Console.WriteLine(a); // 输出0
}
}
栈的结构是先进后出 后进先出,栈底变量的生命周期在栈顶变量之前结束。
栈地址从高往低分配
类型的引用(地址)也存在栈中
二、堆栈详细介绍
1.堆栈
堆:在C#里叫托管堆。
栈:堆栈,简称栈
托管堆不同于堆,它是由CLR(公共语言运行库)管理,当堆中满了后会自动清理堆中的垃圾,因此无需关心内存释放问题。
2.内存堆栈和数据结构堆栈
数据结构堆栈:是一种后进先出的数据结构,是一个概念。
内存堆栈:存在内存中的两个存储区(堆区,栈区)
栈区:存放函数参数,局部变量,返回数据等值,由编译器自动释放
堆区:存放引用类型对象,由CLR释放。
另外,栈的空间较小,而堆内存空间大得多。
ref与out作用都是在方法参数中将参数转为引用类型,按地址传递,方法使用后都会改变原来参数的值。
这两个特性都在一定程度上解决了C#函数只能有一个返回值的问题。
用out或ref修饰的参数都不能带有默认值。
方法定义和调用方法都必须显示使用ref 或 out关键字。
传递到ref参数的参数必须初始化,否则会报错。
传递到out参数的参数必须在方法内部初始化,否则会报错。
ref将参数的参数值和引用都传入方法中,所以ref的参数的初始化必须在方法外部,进行,也就是ref的参数必须有初始化值,否则程序会报错。
out不会将参数的参数值传入方法中,只会将参数的引用传入方法中,所以参数的初始化工作必须在其对用方法中进行,否则程序会报错。
尽管ref和out在运行时处理方式不同,但在编译时的处理方式相同。因此,如果一个方法采用ref参数,而另一个方法采用out参数,则无法重载这两个方法。例如以下两个方法,在编译角度上两个方法完全相同。
public void SampleMethod(int i) { }
public void SampleMethod(ref int i) { }
普通方法:
public void sendMessage(string message)
{
message = "今天是个好日子";
MessageBox.Show(message.ToString());
}
string message = "今天不太好";
sendMessage(message);
MessageBox.Show(message);
结果是message参数作为值参数本身没被改变,改变的是方法里message值参数的拷贝,
因此运行结果是
先弹出 "今天是个好日子" ,再弹出 "今天不太好"
out修饰的方法
public void sendMessage(out string message)
{
message = "今天是个好日子";
MessageBox.Show(message.ToString());
}
string message = "今天不太好";
sendMessage(out message);
MessageBox.Show(message);
用out修饰后发现运行结果是
两次都弹出 "今天是个好日子"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.