C# .NET杂学笔记, 绝对杂七杂八.

Common Intermediate Language CIL

Common Language infrasturcture.  CLI 

Common Language Specification  CLS

Common Type System CTS

Common Language Runtime Environment  CLR

Framework Class Library FCL

National Language Support NLS

Windows Management Instrumentation  WMI

 

mscorlib.dll 定义主要核心类型,  其不会以Side-by-side并存在一个进程中.

 

"first-class function"是这样一种函数:我们的编程语言将它与一般数据类型同等对待。这意味着我么可以将一个函数赋值给一个变量、将函数作为参数传递或者像普通函数一样调用它…C#中可以通过匿名方法创建first-classfunction

Func<string,string> myFunc = delegate(string var1)

{

return "some value";  

};

或者可以用Lambda表达式创建:

Func<string,string> myFunc = var1 => "somevalue";

而以下则构成了闭包.

public static Func<int,int> GetAFunc()

{

    var myVar = 1;

    Func<int,int> inc = delegate(int var1)

                           {

                                myVar = myVar +1;

                                return var1 +myVar;

                           };

    return inc;

}

其实现非常简单,   上述方法会被编译为类,外部调用时,只是普通的类方法调用,当然变量也是类变量.

个人感觉闭包在OO语言中不是特别必要,  在JavaScript中, 很多时候需要回调函数,  比如多个按钮, 设置相应函数是可能有区别,  应用闭包可以每个函数引用拥有不同的变量, 而有不同的行为.

 

.Net编译器会产生IL指令和元数据.

 

IntelliSense智能感知.

 

Assembly  Manifest

 

CSC  C#compiler

AL  applicationlinker.

ILASM.exe  /ILDASM

Obfuscator

mscoree.dll 中定义了_CorExeMain和_CorDllMain例程,   托管的Exe和Dll都会包含X86Stub函数, 其使用此两方法加载.Net环境.

ECMA  EuropeanComputer Manufacturer's Association

 

Just In Time   解释, 本地方法替换(函数地址)

 

托管的优势,  由于托管代码的最后编译环境就是执行环境,所以CLR可以最大限度的利用CPU新指令和环境属性(if(numberOfCPU>1){...})以提高性能.  而本地程序编译会比较保守以支持大多数CPU.

 

Ngen.exe 本地编译

 

AppDomain用于在一个OS进程里执行多个托管应用程序,分别拥有虚拟地址空间.  IIS,SQLServer

Class: Member*(Field/method/property/event)

public/internal(Assembly可见)/protectedinternal(Assembly及派生类可见)/protected(派生类可见)/private

CLS只是CLR/CTS的子集, CLS保证所有语言的可用性. [assembly:CL$Compliant(true)]

csc.exe /out:App.exe /t:exe /r:mscorlib.dll  App.cs

/target:winexe/exe/library/module

 

module是一个不包含清单的PE文件, 扩展名为.netmodule,其被使用前必须加入程序集.  好处是可以将较少使用的资源单独生成文件以减少网络下载.

csc.exe /t:module a.cs

csc.exe /t:library /addmodule: a.netmodule b.cs  

 

addmodule加入清单.

/embed[resource]

/link[resource]

/win32icon /win32icon

 

弱命名只能进行私有部署.  强命名即可私有(codebase)也可全局部署(gac).

sn.exe  strong nameutility.

延迟签名,  允许仅使用公钥进行简明,  用于开发期间,   保护私钥.

 

side by side 允许程序集同时引用多个版本的Dll

using aLib=a.lib;  重命名  a.lib

checked 启用基本数值溢出检测,   其指示编译器使用不同的指令. 故对其内部的方法调用无效.

class 在堆(赋值指针拷贝), Struct在栈(赋值深拷贝)

new Struct()也是合法的, 且CLR会将基本类型实例赋缺省初始值.   而Struct v;  CLR 则认为其field未赋初值.但并非Null.

类/结构布局.   StructLayoutAttribute,   类(Auto对齐)|结构Sequence,如与非托管代码交互,一般需要明确的排序.

自动拆装箱.   对于Struct,  装箱操作会在堆中分配内存, 生成引用实例. 然后拷贝Fields.老的Struct值可以重用直到栈帧回收.拆箱严格的说并不拷贝字段,但一般程序中都包含赋值操作.很明显自动拆装箱损害了性能.一般仅用于与非托管代码交互,或确定无方法间传值,无自动拆装箱.

Point p;//struct p.x=1;p.y.2

ArrayList.add(p)  //需要引用类型, 自动装箱

Console.WriteLine(p); ///需要引用类型, 自动装箱

Point p2=(Point)ArrayList[0];  //自动拆箱 , 并赋值.

 

ReferenceEquals 在类未重载==符号时,可以简单的使用  == 替换. 

 

类的修饰符: abstract sealed.

字段的修饰符: Static readonly(仅可在构造器赋值)

vitual允许子类替换,  new子类不替换基类方法,只是隐藏.  override重写基类虚方法.

const常数只允许基本类型, 编译时替换. 如果某模块的数值需要运行(非编译)时被另一模块获取,则不能用常数, 可用Readonly,运行时构造器赋值.

 

如果存在很多需要初始化的实例字段和重载的构造方法,  应避免在定义时初始化,因为编译器会将定义时初始化指令移至每一个构造器中,造成代码尺寸增大.可在默认构造器中初始化实例,其它构造器调用默认构造器.

 

System.AppDomain.DomainUnload()

System.AppDomain.AssemblyLoad

System.AppDoamin.UnhandleException|AssemblyResolve/ResourceResolve/TypeResolve.

 

操作符运算,  CLR并不知道操作符,  即使对于4+3-2这种语句. 对于类类型,  操作符/转换操作符重载只是生成了一些对应特殊*方法,  编译器将源代码中的符号换成了方法调用; 对于基本类型, 则是转成汇编指令了.

 

CLR支持类型定义多个仅返回值类型不同的方法(但目前仅IL汇编支持). 转换操作符(explicit/implicit)重载利用了这点,生成多个仅返回值类型不同特殊方法以支持转换成不同的类型.

public static explicit operator Int32(ClassA)  {return it32;}    

//public static Int32 op_Explicit(ClassA a);

 

public static explicit operator Single(ClassA) {returnsingle;}  

//public static Single op_Explicit(ClassA a);

 

CLR方法参数缺省是按值传递的,   值类型拷贝值, 引用类型拷贝指针地址.  CLR还允许按引用传参, out/ref会使编译器做额外操作,out调用方法前不需要初始化,被调用方法也不能读取其值,但被调用方法必须赋值.  ref要求先初始化, 方法内可读可写.  从IL/CLR角度看,  其并无区别. 只是编译器会做检测.  是否out/ref可以作为重载的区别,但仅out,ref之间不能, 因为在CLR眼中他们是一样的.

 

可变参数  params

内联, 空间换性能.  

 

属性, 编译器会自动加入setter/getter方法, 以允许不支持属性的语言访问.  value 关键字表示要赋的新值.  indexer含参属性.

 

Delegate.Combine/Remove

 

Event  谁注册谁,谁负责解除注册.

 

定义事件步骤. 1,EventArgs. 2,Delegate Eventhandler. 3,  定义Event Member. 4, 保护的虚方法(子类可加入额外操作,或停止通知),通知注册的对象.

3,定义Event Member ,编译器会自动加入同步的Add(+=)/remove(-=)方法用以处理事件注册.  +=/-+不会抛出异常.

 

Char.GetUnicodeCategory(),可以得到是否为大写/小写/符号....

 

String  基本类型????

其直接继承自Object, 是引用类型, 内存分配在堆上.但C#一般认为其实基元类型,可在源代码中用文本变量直接表达.常量也会放在Assemply元数据中. 不能使用newString(""), 但接受new String(char[] a)等构造. String是个特殊的应用类型,CLR知道且能直接访问其内存布局.但也因此被定义为Sealed类型.

String的不变性使得其不会出现线程同步问题,  相同值可以内存共享.

 

String.Compare/CompareOrdinal,  后者只比较码值(性能高),  前者考虑CultureInfo值, 有些码值不同的字符串在某些Culture中逻辑上会被认为是相等的.

String.CompareTo与Compare相似, 但不能指定CultureInfo, 

 

InvariantCulture无特殊语言文化.

Thread.CurrentThread.CurrentCulture.

 

String比较非常常见,通常性能较低.  借助于字符串驻留(String Interning)可以提高比较性能.String.Intern()

字符串驻留是进程级的,可以跨应用程序域而存在。垃圾收集不能释放哈希表中引用的字符串对象,只有进程结束这些对象才会被释放

String Pooling与String Interning相似, 只是其存在与编译生成的Assemply中,而后者是在运行时..

 

值类型最好重新实现Equals方法和重载==操作符,因为默认情况下实现的是引用相等

 

ReferenceEquals方法比较两个值类型一定返回false

Object.MemberwiseClone方法执行对象的浅拷贝

 

==/String.Equals/StringInst.Equals

静态比较会首先比较两个引用是否指向通一对象.在字符串驻留机制下会极大的提高性能.

重载==操作符与静态Equals一致.

实例Equals会内部使用CompareOrdinal比较码值.

 

@"xxxx",  @只是编译器不进行转义,包括回车在内的特殊符合可直接使用.

 

分代式内存回收机制使得生存期短的对象很快且代价较小的被回收, 所以通常不需要优化算法以提高性能.

Object.toString()提供了一个将类型实例转为字符串的方法.但其不能对字符串格式进行控制,不能根据Culture进行格式化.

实现System.IFormattable接口(  ToString(format,formatProvider) )可以为类提供更灵活的字符串转化方法..NET基本类型,Datetime,枚举..都实现了此接口.

 

共有3个类实现了formatProvider接口.  CultureInfo(其包含后两者),NumberformatInfo,DateTimeFormatInfo.

 

Sting.format("{0},{1:D}",  arg0, arg1) 组合字符串.  {1:D} 指定 以"D"Format 格式化参数1.   即调用arg1.ToString("D"),  其还支持IFormatProvider参数.

 

StringBuilder.AppendFormat与之类似, 但仅能追加.  Consolw.WriteLine/Write也相似,但不支持IFormatProvider参数.

 

Parse.  Int32.Parse(..)   DateTime.Parse(...)   NumberStyles,DateTimeStyles枚举指定了解析的样式.

 

Int64 var=(Int64)System.Windows.Forms.Control.Invoke(newFunc<Int64>(() => Convert.ToInt64("64")));

 

AsyncOperationManager.SynchronizationContext.Post/Send

 

System.Text.Encoding.UTF8, Encoding.GetEncoding('gbk').   系统会缓存以保证只构造一个特定编码对象.

StringInfo用于处理32位Unicode码.

 

Encoding.getEncoder/Decoder. 

 

System.Net.Sockets.NetworkStream

 

Convert.ToBase64String, Convert.FromBase64String

 

Convert|BitConverter

 

IConvertible: toBoolean/toChar/toString/toUInt32.....

 

IComparable: compareTo(object obj)

IComparer:   compare(T x, T y)

ICloneable

IEnumerable

IFormattable

枚举不过是定义的一组常数,  但在编译期,  它作为强类型值提供了可读性,和安全性.

 

Enum可以继承于基础类型,  如Byte ,Int32,  使用staticEnum.GetUnderlyingType(typeof(enumInst)) 得到基类型.

EnumInst.toString("Format")   'D'十进制,'X'十六进制.

Enum.Parse(...) 解析生成枚举类型.

 

[Flags/FlagsAttribute]  //当此标记应用在枚举类型时(必须显式赋值),枚举将是位标记集合.可以做位与/非/或操作.

enum Actions{

Read =0x0001,

write =0x0002,

delete =0x0004

}

 

数组隐含继承自System.Array,  其实现了ICloneable,IEnumerable,ICollection,IList使我们可以方便的使用之.

一维0基数组又称为Vector.

一维数组   Int32intArray=new Int32[4];  性能最好, 有专用的IL指令

多维数组   Int32[,]intArray=new Int32[4,6];

交错数组   Int32[][]intArray=new Int32[4][];  元素为数组的数组.Jaggedarray

 

对于元素类型为相容的引用类型, 维数相同的数组, CLR允许隐式或显示的转换为另一类型,但写入时非常容易出错.看上去父类数组引用可以加入任何对象,但由于其实际只能包含子类数组定义元素.

Object[,] o2dim=new  FileStream[3,10];

 

值类型数组不能直接转换,如long[]= new int[3]非法; 可用Array.Copy()进行拷贝.  将Int32[]拷至Object[]会导致装箱.  反之则拆箱.

 

强烈推荐返回数组的方法,  不返回Null而返回空数组.

Array.CreateInstance可以创建非0基数组, 运行时确定元素类型,维数.

 

fixed  * &  /unsafe 非安全代码.

 

派生类自然可以重写父类的方法. 但是我们究竟要怎么办?  Liscov principle.

.net选择了以下继承关系.

System.IO.Stream,System.IO.FileStream,System.IO.MemorySteam, Steam.NetSockets.NetworkStream

System.Windows.Forms.Control, Button,Checkbox,ListBox

 

.Net中的集合类使用了基于接口的设计. IEumerable,ICollection,IList,IDictionary....  ArrayList,Hashtable,Queue,SortedList

 

定制标记.  继承自System.Attribute.

[System.FlagsAttribute允许将枚举看作一组标记.

System.SerializableAttribute允许某字段可被序列化.

StructLayout(LayoutKind.Sequential)

DllImport("Kernel32")

MarshalAs  用于与本地程序通信,  定义结构类型的大小,  如C#和与C++中char[], String的映射.

InAttribute

OutAttribute

CLSCompliant

Conditional

DefaultMemberAttribute

HandledProcessCorruptedStateExceptions

STAThread 只用于COM, 保证COM对象是线程安全的.

 

[AttributeUsage(AttributeTargets.Enum,Inherited=false)]

public class FlagsAttribute:System.Attribute{

                publicFlagAttribute(){}

}

大部分系统常见的Attribute,  处于减少元数据大小目的,以一位标识特性,  称为伪定制特性.  isDefined,GetCustomAttributes不能得到他们, 但提供了IsSerializable,isAutoLayout,..方法检测.

 

System.Reflection

 Assembly,Module,Enum,ParameterInfo,MemberInfo,Type,MethodInfo,ConstructorInfo,FieldInfo,EventInfo,PropertyInfo

 

Delegate是一种回调函数机制,是一种对方法的封装,与传统基于指向函数的指针相比,  它是类型安全的, 并提供了按序调用多个方法的能力.

 

类是有行为的数据,闭包是有数据的行为。                                                

"闭包",是指拥有多个变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。 我们简单的可以理解为闭包是个函数,而它 "记住了周围发生了什么"。表现为由"一个函数"体中定义了"另个函数"

 

Sytem.MulticastDelegate是所有委托的父类.Sytem.MulticastDelegate有继承自Sytem.Delegate(单播), Sytem.Delegate除静态的Combine/Remove方法外以很少使用.

DefaultConstructor/BeginInvoke/Invoke/EndInvoke.

 

当调用Delegate实例时, 编译器将其转化为调用Invoke方法,但C#从语法上不允许显示调用Invoke方法.  当委托按序调用多个方法时, 仅能得到最后一个的返回值.

 

Delegate重写了Equals/==/!=方法, 当其Target,MethodPtr字段相等时即为相等.

 

在使用Sytem.Delegate.Remove时,如果最后一个被删除,   引用即为Null, 所以总要需要检测委托引用为空.如果是Event委托, 则不必检查为空.

 

委托+=/-= 会调用Sytem.Delegate.Combine/Remove方法.  此两方法不会抛出异常.  如删除不存在, 添加已存在.

 

.net 提供了Delegate.createDelegate(...), 允许动态创建委托,类似Array.CreateInstance在运行时确定委托的参数,返回值.

 

Delegate.DynamicInvoke 允许运行是传递一组参数来调用委托对象的回调方法.

 

System.IO.MemoryStream

 

Type typeVar=typeof(typefullname)

 

 

OutOfMemoryException 在应用程序试图新建对象,而GC无法分配时, 可以捕获此异常;而当CLR无法分配其内部需要的内存时,程序会立刻退出.如果设计服务器程序时,可以有一个独立的守护进程(Watchdogprocess),发现服务器中断时,自动重启服务.

StackOverflowException与OutOfMemory类似,但其即使在应用程序栈溢出是,Finally块也不会执行,因为Finally需要额外的栈空间.

 

using 用于实现了IDisposable接口的实例, 编译器会自动转化为tryfinally(x.Dispose())指令.

 

FCL中很多类都实现了IDisposable接口(如FileStream)  , 并且提供Close别名,  就是说FileStream.Dispose()与FileStream.Close()是一样的.

 

一个最佳实现是, 仅catch可以预见的异常,  而不是简单的捕获Exception,  因为任何情况都可能出现StackOverflow/OutOfMemory异常, 程序可能会以一种不可预期的结果继续前行.

Java可此类为Error, 并无此问题, 但也不推荐捕获Exception,而绝不捕获Throwable.

 

为什么要自定义异常,  假如一个类的方法会根据姓名返回电话号,   其内部实现可能是网络/文件/数据库查找, 可能抛出的异常可能是文件IO/SQL/Network异常,  当时调用者无需知道这些, 如果自定义一个NameNotFoundException更合理些,  但一般会将原异常设为内部异常,以便调查RootCause.  但假如这个类也提供了设置电话本文件的路径,  也就是说调用者知道这一定是基于文件的实现,根据姓名返回电话号方法就不应该捕获任何异常,而让其向上抛出,这就是抽象级别的不同.

 

在Catch块中使用无操作数Throw, 会抛出捕获的异常.

 

AppDomain.CurrentDomain.UnhandleException+=newUnhandledExceptionEventHandler(method1(sender,unhandledExceptionEventArgs))

 

unhandledExceptionEventArgs.IsTerminating  标识CLR是否会因为此异常中断程序.  通常对于线程池,终止化线程,CLR会忽略, 而主线程,手工或非托管线程IsTerminating则是True.

 

与此对比,   Java线程的未捕获运行异常只会导致当前线程退出,不会影响进程.  

 

.Net无RuntimeException/Non-RuntimeException概念,   所有异常都是RuntimeException.

 

System.Diagnostics.Debugger.Launch()加载调试器.

 

主线程  Main

手工线程 System.Threading.Thread  Thread t=new Thread(newThreadStart(methodName));

线程池  ThreadPool ,Timer, Delegate.BeginInvoke/EndInvoke,  主要用于短时间操作.其中线程分两类1000*IO/25*worker,前者用于异步IO.如异步FileStream.  后者为普通线程. 

终止化器线程  托管堆用于专门执行Finalize()的线程.

非托管线程

 

thread-local storage(TLS)

 

Thread.CurrentThread;

SynchronizationContext.current.Post()允许一个线程和另外一个线程进行通讯,SynchronizationContext在通讯中充当传输者的角色。另外这里有个地方需要清楚的,不是每个线程都附加SynchronizationContext这个对象,只有UI线程是一直拥有的.

 

 "在Window窗体程序开发的时候,如果使用多线程编程,在子线程中访问主线程窗体内的控件,就需要使用控件的Control.Invoke方法或者BeginInvoke方法。但是有时候因为Window执行速度太快,尤其是你写代码的时候在InitializeComponent();完成之前起了一个线程去执行某些操作,涉及到窗体控件的,当你在调用Control.Invoke的时候,就可能出现  "在创建窗口句柄之前,不能在控件上调用 Invoke 或BeginInvoke "错误。

 

解决的办法就是让线程等待,直到窗口句柄创建完毕:

//防止在窗口句柄初始化之前就走到下面的代码

while (!this.IsHandleCreated)

{

    ;

}

创建的窗体没有注销该事件,把之前所有的窗体都注销该事件

BeginInvoke方法的调用语句前再加一句:IntPtr i = this.Handle;就OK了,这比死循环配合this.IsHandleCreated的判断方法更简洁,因为this.Handle这个属性本身就对应一个方法,取不到句柄,程序就不会向下进行。

 

if (_form.Handle.ToInt32() > 0)

{

  _form.Invoke(method, args);

}

 

Winform布局: Dock & Anchor

BackgroundWorker在WinForm中会以UI线程执行DoWork,其调用用SynchronizationContext.Post().在Winform中UI线程SynchronizationContext引用实例为WindowsFormsSynchronizationContext,其将事件封装入UI线程.  在非UI程序中有另外的实现.

 

Winform 程序中System.Windows.Forms.Application.Run()负责Form的消息循环, 其会将窗体消息分发给System.Windows.Froms.NativeWindow的WndProc.

 

System.IO. MemoryMappedFiles 可以建立独立的文件做内存映射,也可以使用系统的分页文件.

 

lock和[MethodImpl(MethodImplOptions.Synchronized)]都是用Moniter.Enter实现的.Moniter.Enter属于.Net特别的轻量型同步机制,不会出现用户/内核态切换. 而Mutex, Semaphone,AutoResetEvent, ManuResetEvent等继承自WaitHandle的对象均属内核对象,调用时需要切换,但能跨进程同步.

 

锁很复杂,  需要考虑的很多,  程序代码的同步, 事务(如DB)同步, 离线锁,  乐观(如Java中的旋转锁/AtomInteger/CAS)/悲观.  

 

vs+sos

.load sos.dll

!dso  //dump stackobjects

!do  //dubmp objects

!!dumpmt -md

 

匿名方法.

books.Find(delegate(Book book){return book.Price < 50;});

books.Find(book=>book.Price<50);

 

Shared Source CLI 2.0 (开发代号Rotor) 是微软.NETFramework 2.0的Shared Source实现版本。Shared Source是微软推出的源代码共享计划,可以在一定限制的情况下获得/使用源代码.

 

reserve ,指分配保留地址空间.   必须在提交commit 物理内存后才能使用.

 

new 指令会计算类所有字段所需的字节数. 另加上两个附加字段,  方法表指针, SyncBlockIndex.  托管堆分配内存是连续的,  就是说连续创建的对象会被放在一起,其有一个NextObjPtr指针, 指向下一个需要分配实例的地址.这样托管堆分配极快,甚至比肩栈空间分配速度.  而C/C++堆多是基于链表的.内存不连续,且容易出现碎片,堆分配内存代价较高.     在应用程序中相近时间分配的对象经常被同时访问,  缓存的命中率高, 从而又提高的性能.但是程序地址空间和存贮空间不是线性无限的,内存需要释放,GC需要保证内存被释放,空余内存是线性的.此对性能的影响很可能抵消了原来说的好处,  但至少减少了程序员的负担.

 

实际上, CLR并不保证对象在一个方法的整个生存期都存活.JIT编译器会根据变量在方法中的引用决定其是否可达,  例如在一方法中引用变量在第4行就不在被使用了,  JIT会在4行指令之后标识其不可达, GC可以回收.    但isJITOptimizerDiabled/Debug属性会指示JIT编译其将变量的生存期扩展至作用域末端,于是在程序调试时.我们总能看见作用域内的对象.

 

~className()   析构方法.  在对象被GC回收前调用, 可用于清理封装的非托管资源.其会被编译器编译为下面方法,并将其内指令放入try/catch/finally(*)块内.

protected override void Finalized()

 

GC.SuppressFinalize(Object) 通知GC不调用Object的析构方法.

 

Dispose(boolean disposing) disposing一般指对象正在被显式关闭,  而非执行析构.   ~className()析构方法一般传入false,告诉它不应该执行任何引用其它托管对象的代码;  而显示调用传入true.

 

但我们应该避免依赖析构方法,  不同于C++,  .Net平台的GC是不确定的,  Finalized只在GC时调用, 无法确定其时间,且其极大的损伤的程序性能.

 

CLR使用专门的线程调用Finalize方法, 如果一个Finalize方法有无限循环,这个线程被阻塞,其它Finalize方法得不到调用.而GC需要保证Finalize后内存再回收. 所有自定义了Finalize方法的实例将不能被GC回收.  没有自定义此方法的将不被认为需要Finalize线程管理.

 

当进程正常退出时,  CLR关闭前会试图调用堆中所有对象的Finalize方法, 当任一方法运行超过2S或所有方法超过40S,CLR直接退出,剩余Finalize方法不再调用.

 

对于管道依赖的IO流, 如NewBinaryWriter(New FileStream("FileName"));   只要显示关闭外部的流就可以了.

 

System.Environment.HasShutdownStarted

 

 

弱引用WeakReference 

 

大尺寸对象使用特殊的GC策略.  不会被压缩,  直接在2代.

 

服务器版本执行引擎. MSCorSvr.dll

工作站版本执行引擎. MSCorWks.dll

<GcConcurrent>  指定公共语言运行库是否在单独的线程上运行垃圾回收

 

Assemply.GetExecutingAssembly()

AppDomain.CurrentDomain.GetAssemblies()

Assembly.Load/LoadFrom

 

System.Activator.CreateInstance

 

C:\WINNT\Microsoft.NET\Framework\version\csc.rsp文件定义了缺省的响应文件,其包含了缺省引用的包.

csc /noconfig 取消使用此缺省响应文件

 

System.Console.ForegroundColor/BackgroundColor/WriteLine

Environement.Version/GetCommandLineArgs/ProcessorCount/...

System.DateTime/System.TimeSpan

System.Numerics.BigInteger/System.Numerics.dll

 

@逐字字符串.

 

checked{}|unchecked{}局部生效

csc.exe /checked 全局生效.

 

var x=2;  编译器根据初值推测x的类型. 只用于局部变量.  一般只用于LinQ,foreach.

var subset= from i in numberArray where i<10 select i;

 

switch 支持字符串.

 

typeof(className),  inst.GetType()

 

Method1(String message, Stringowner="programmer")  可选参数. 其值必须在编译期能确定.如Method1(Stringmessage, DateTime dt=new DateTime.Now)  则不能通过编译.

 

System.Array.Clear/CopyTo/Length/Reverse/Sort...

 

结构可以直接复制, 变量域被复制.   Point b = pointA;

 

int?  nullablInt=20;//?可空值类型.  实际仅为 Nullable<int> nullablInt=20;简化表示.  Nullable为结构值类型. 并且要求包容的也是值类型

对Nullable<T>执行GetType方法,将返回类型T,而不是Nullable<T>

 

判断nullablInt是否为空, 用nullablInt.HasValue;

 

b=a??2;  //当a为Null时, b赋值为2;

 

 

static class ClassA  静态类,  无法实例化, 只能使用其静态方法.   //考虑singleton.

 

OO: encapsulation/inheritance(is-a)/polymorphism

square is a shape.  acar has a radio. (has-a composition)

 

属性.

private int a;

public  int apropertya{

 get{return a;}

 set{a=value;}   //如果不定义set, 则为只读属性.

}

自主属性.

public int a{set;get} 不可以去掉set 或get以实现只读/写属性,  但可加Modifier, 如publicint a{private set;get}

自主属性无法直接指定初始值, 值类型有各自的缺省初始值,  引用类型就是是Null了,  可在构造方法中为自主属性.

 

对象初始化器语法.

new ClassName{x=10;y=20;}   隐式调用无参构造函数,  并设初值.

new ClassName(2,3){x=10;y=20;}  显示调用构造函数,  并设初值.

System.Console.WriteLine()

System.Console.ReadLine()

 

类似JavaDoc,  /** 多行注释 **/  ,    ///单行注释 可以被存储到一个单独的XML帮助文档中. 

 

System.Array.xxx工具方法.

 

goto可在switch语句中实现贯穿,  从一个块执行到另一个.

 

C#预处理

#if,  #else, #define,#error(指示编译器报错),#warning, #pragma

可选参数  void method(inta,  string b, string=”default”)

 

扩展方法.

static class ExtendedClass{

   public static void CopyTo(thisExtendingClass, string param1,string param2)

}

partial class/partial method

声明partial method后, 可在本部分引用调用.  可以在其它部分实现此方法; 当也可不实现, 编译器会忽略方法的调用.

partial void method(params….)  //返回值必须为空.

接口隐式/显式实现

显式实现调用时 引用变量必须为接口本身,不能是实现类.

    class Class1:Interface1

    {

        void Interface1.Method1()//方法前要加接口名.

        {

            throw new NotImplementedException();

        }

        }

显式接口实现更多的在考虑语义上概念,  接口代表一种契约,显式实现强迫 更明确的使用契约.  程序设计,CleanCode在很大程度上,就是要得到明确的,显式的结构,显而易见的结果.

monitor.enter/exit 需要使用引用类型对象,      当传入值类型时就会自动拆装箱,  而enter/exit必须成对出现.  

弱引用WeakReference.

System.GC.SuppressFinalize

泛型约束 Class Tree<T>  where T: baseType

泛型方法  声明:public  T method1<T>(param1,param2);    调用: method<String>(param1, param2);

.Net提供了一系列名为Action(无返回值)/Func(最后一项为返回值)的预定义泛型委托.此种通用委托减少了总需要自定义委托的麻烦,使得编码变得简单,但其缺乏明确的定义,不能显式的提供有意义的名字.

语句Lambda,  由…推导出.   用于定义匿名方法,  Lambda为语句块.

表达式Lambda, 满足…条件,  多用于Linq.Enumerable扩展方法,  e.g. Names.Where(name=>name.Contains(“”))  ; 像是返回布尔值的匿名方法.

Lambda能使用定义其位置的可见变量,    也即可形成闭包.

当把Lambda表达式赋值给委托类型变量,    编译器即把它编译成委托类型, 即是一个函数.

当把Lambda表达式赋值给Expression,  它即是一个树状的表达式,   虽然可以把它编译成委托实例, 当更多的是: 表达式树要转化为不同的格式或指令.

Lambda本身除非被赋值/转型, 否则没有类型.

多播委托被调用时, 链表中的委托实例会被顺序调用,  但只返回第一个的返回值.  只要有一个引发异常, 后面的就不会被调用.  如果必须处理所有链表项, 则需要手动遍历列表,并单独调用.  MultiDelegate.GetInvocationList().  这样还可以得到所有委托调用的返回值.

 

Event关键字强化了委托链的实现.

1,封装订阅,   只能使用+=/-=添加/删除订阅者,  赋值=被禁用.  这样不会出现链表被以外重置.

2, 封装发布, 只有委托链表实例的类才能调用它.

C#编码规范建议event委托使用两个参数,   (object sender,EventArgs  arg)

集合初始化器.  集合类实现了ICollection接口或实现了IEumerable并存在Add方法.

new List<String>(){

“stringa”,

“stringb”,

“stringc”

}

实现了IEnumerable<T>或IEnumerator<T>接口的类即为集合类,  可以支持遍历. 从而支持foreach,Linq标准查询运算符.

迭代器(IEnumerator)在遍历过程中需要保持状态,  多线程遍历则会引发问题, 所有一般集合类不直接支持IEnumerator,而是实现IEnumerable,  它唯一的方法GetEnumerator()返回一个新的迭代器.在多线程中,避免了冲突.

数组类实现了IEnumerable接口.

Linq并行运行, list.AsParallel().Select(….)   AsParallel指示并行运行.

 

查询表达式Linq, 编译器会将其转为linq.Enumerable标准查询运算符

from word in keywords where word.Contains(“*”) select word;

 

ServicePointManager.ServerCertificateValidationCallback +=(sender, cert, chain, sslPolicyErrors) => true;

 

System.GC

 

Form.Close 方法

窗体关闭后,关闭在该对象内创建的所有资源并且释放该窗体。 通过处理Closing 事件,并设置作为参数传递给事件处理程序的CancelEventArgs 的 Cancel 属性,可以防止在运行时关闭窗体。 如果要关闭的窗体是应用程序的启动窗体,则该应用程序结束。

 

在以下两种情况下调用 Close 不会释放窗体:

(1) 窗体是多文档界面 (MDI) 应用程序的一部分且是不可见的;

(2) 您是使用 ShowDialog 显示的该窗体。 在这些情况下,需要手动调用Dispose 来将窗体的所有控件都标记为需要进行垃圾回收。

 

SystemInformation.UserInteractive 如果当前进程在用户交互模式中运行,则为true;否则为false。

 

如果类要支持foreach,   则需要实现IEnumerable返回IEnumerator实例或直接实现IEnumerator(数组,List..都实现了IEnumerable接口). 可以用yield来构建迭代器(IEnumerator).

 

yield return a;

yield return b;

yield return c;

即返回一个a,b,c的迭代器.

 

 

IEnumerable<T>  读作  IEnumerable of T

 

IComparable   /IComparable<T>

 

 

泛型方法.  Array.Sort<int>(intArrayInst)

 

配置文件(Configuration File)大致上可分为以下3种。

ASP.NET 应用程序配置文件(Web.config)。

应用程序配置文件(App.config)。

计算机配置文件(Machine.config)。

 

System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile= newFilePath;

AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE",newFilePath);

 

in .Net,  there is 2methods to access visual control  innon-UI thread.

 

1.

//declare a delegate,DoUpdateUI like below

delegate void UpdateUIDelegate(TextBox tb, string result);

//use control.invoke to send UI instructions to UI thread.

private void DoUpdateUI(TextBox tb, string result)

{

  if (tb.InvokeRequired)

  {

    UpdateUIDelegate d = new UpdateUIDelegate(DoShowUI);

    this.Invoke(d, new object[] { tb, result });

  }

  else

  {

    tb.Text = result;

  }

}

2.

private void btnTest_Click(object sender, EventArgs e)

{

  // get UI thread' SynchronizationContext reference.

    SynchronizationContextcurrentAsync = SynchronizationContext.Current;

   ThreadPool.QueueUserWorkItem(Execute, currentAsync);

}

 

private void Execute(object status)

{

  SynchronizationContext currentAsync = status as SynchronizationContext;

   if (currentAsync !=null)

   {

   //send ui instructions to UI thread.

    currentAsync.Post((obj) => { lbStatus.Text = obj.ToString(); },"success!");

   }

 }

 

lock(lockObject)

{

    myField++;

}

 

Monitor TryEnter(this, 300)) ,Wait(), Pulse().具有线程关联。 也就是说,进入监视器的线程必须通过调用Exit 或Wait 才能退出

 

volatile

System.Threading.Interlocked.Increment|Decrement|Exchange  保证操作数操作的原子性.

lock

System.Threading.Monitor.Enter|TryEnter|Exit|Wait与lock相比操作块更灵活,  都使用轻量锁.

当调用Wait方法时,线程释放资源的独占锁,并且阻塞在wait方法直到(另外的线程获取资源的独占锁后,更新资源信息并调用Pulse方法)后返回。

 

由于字符串的拘留池机制,建议不要lock字符串。

 

System.Threading.ReaderWriterLock|System.Threading.ReaderWriterLockSlim

 

 [System.Runtime.Remoting.Contexts.Synchronization]

 public classSynchronizedClass : System.ContextBoundObject

 {

 }

 

.NET 1.0 System.Collections ArrayList/Hashtable    非线程安全, 但提供线程安全Wrapper,  SyncRoot

.NET 2.0 System.Collections.Generic  泛型版本Collections,不提供任何线程同步;当同时在多个线程上添加或移除项时,用户代码必须提供所有同步.  相比非泛型版本,  提供了类型安全,在元素是值类型时,无需拆装箱,极大的提高了性能.一般意义,如果运行环境是.NET2.0或更高版本, 不使用非泛型集合.

.NET 4.0 System.Collections.Concurrent

 

SpinLock

在多核计算机上,当等待时间预计较短且极少出现争用情况时,SpinLock 的性能将高于其他类型的锁,它不可重入,任何重新进入锁的尝试都会导致死锁,并且死锁非常难调试.通过使用一个采用布尔输入形参的SpinLock 构造函数并传入true 实参,可以启用线程跟踪模式。 完成开发和测试阶段之后,应关闭线程跟踪模式以获得更好的性能。

 

 "同步事件",它是有两个状态(终止和非终止)的对象,可以用来激活和挂起线程。 让线程等待非终止的同步事件可以将线程挂起,将事件状态更改为终止可以将线程激活。 如果线程尝试等待已经终止的事件,则线程将继续执行,而不会延迟。

 

同步事件有两种:AutoResetEvent 和 ManualResetEvent。 它们之间唯一的不同在于,无论何时,只要 AutoResetEvent 激活线程,它的状态将自动从终止变为非终止。 相反,ManualResetEvent 允许它的终止状态激活任意多个线程,只有当它的 Reset 方法被调用时才还原到非终止状态。

 

ReaderWriterLock 类

 

Semaphore waitone()  局部信号量和已命名的系统信号量

SemaphoreSlim wait() 提供不使用 Windows 内核信号量的轻型信号量类。 通常,当等待时间较短并且只有在尝试了原始同步类型并发现它们并不令人满意时,才应使用这些类型。 在需要跨进程通信的方案中不能使用轻量类型。

System.Threading.Timer 类可用于定期在单独的线程上运行任务,当System.Windows.Forms.Timer 对象不可用时(例如在开发控制台应用程序时),线程计时器特别有用。

 

委托是可保存对方法的引用的类。 与其他的类不同,委托类具有一个签名,并且它只能对与其签名匹配的方法进行引用。 这样,委托就等效于一个类型安全函数指针或一个回调。

 

如果希望您的类引发一个事件,您需要提供以下三个元素:

提供事件数据的类。

事件委托。

引发事件的类。

 

委托对象的一个有用属性是:可以使用 + 运算符将多个对象分配给一个委托实例。 多播委托包含已分配委托的列表。 在调用多播委托时,它会按顺序调用列表中的委托。 只能合并相同类型的委托。

 

匿名方法

delegate void TestDelegate(string s);

TestDelegate testDelA = new TestDelegate(MethodA);

 

 

// Create a handler for a click event.

button1.Click += delegate(System.Object o, System.EventArgse)

                   {System.Windows.Forms.MessageBox.Show("Click!"); };

 

System.Threading.Thread t1 = new System.Threading.Thread

      (delegate()

            {

               System.Console.Write("Hello, ");

               System.Console.WriteLine("World!");

            });

           

"Lambda 表达式 "是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型.

许多标准查询运算符都具有输入参数,其类型是泛型委托的 Func<T, TResult> 系列的其中之一。Func<T, TResult> 委托使用类型参数定义输入参数的数目和类型,以及委托的返回类型。始终在最后一个类型参数中指定返回值。

 

Lambda 表达式 (input parameters) =>expression

Lambda 语句与 Lambda 表达式类似,只是语句括在大括号中:  (input parameters) => {statement;}

 

在编写 Lambda 时,通常不必为输入参数指定类型,因为编译器可以根据 Lambda 主体、基础委托类型以及C# 语言规范中描述的其他因素推断类型。 对于大多数标准查询运算符(LinQ),第一个输入是源序列中的元素的类型。

因此,如果要查询 IEnumerable<Customer>,则输入变量将被推断为Customer 对象,这意味着您可以访问其方法和属性:

customers.Where(c => c.City == "London");

 

ExpressionVisitor 修改表达式树

 

只能执行表示 lambda 表达式的表达式树。 表示lambda 表达式的表达式树属于LambdaExpression 或 Expression<TDelegate> 类型。 若要执行这些表达式树,需要调用 Compile 方法来创建一个可执行委托,然后调用该委托。

 

如果委托的类型未知,也就是说,lambda 表达式属于 LambdaExpression 类型,而不是属于Expression<TDelegate> 类型,则必须对该委托调用 DynamicInvoke 方法,而不是直接调用该委托。

 

如果表达式树不表示一个 lambda 表达式,则可以通过调用Lambda<TDelegate>(Expression, IEnumerable<ParameterExpression>) 方法,创建一个以原始表达式树作为其主体的新lambda 表达式。 然后,即可按照本节前面所述执行该 lambda 表达式。

 

Expression<TDelegate> 类型提供Compile 方法,该方法将表达式树表示的代码编译成一个可执行委托。

 

            Expression<Func<int,bool>> lambda = num => num < 5;

           Func<int, bool> lama=num => num < 5;

            vardf=lambda.Compile()(3);

            var dfd =lama(3);

 

延迟初始化 线程安全

// Initialize by using default Lazy<T> constructor.The

// Orders array itself is not created yet.

Lazy<Orders> _orders = new Lazy<Orders>();

 

// Initialize by invoking a specific constructor on Orderwhen Value

// property is accessed

Lazy<Orders> _orders = new Lazy<Orders>(() =>new Orders(100));

 

在必须延迟初始化大量对象的方案中,您可能会认为在 Lazy<T> 中包装每个对象需要过多的内存或过多的计算资源。 或者,您可能对如何公开延迟初始化有严格的要求.

// Lazily initialize the orders without wrapping them in aLazy<T>

       LazyInitializer.EnsureInitialized(ref _orders[i], () =>

            {

                //Returns the value that will be placed in the ref parameter.

                returnGetOrderForIndex(i);

            });

 

 

Daemon/background线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

 

ThreadPool

BackgroundWorker

.NET 允许您异步调用任何方法。 为此,应定义与您要调用的方法具有相同签名的委托;公共语言运行时会自动使用适当的签名为该委托定义BeginInvoke 和 EndInvoke 方法。

 

1.进行某些操作,然后调用 EndInvoke 一直阻止到调用完成。

 

2.使用 IAsyncResult.AsyncWaitHandle 属性获取WaitHandle,使用其 WaitOne 方法阻止执行,直至 WaitHandle 收到信号,然后调用EndInvoke。

 

3.轮询由 BeginInvoke 返回的 IAsyncResult,以确定异步调用何时完成,然后调用EndInvoke。

 

4.将用于回调方法的委托传递给 BeginInvoke。 异步调用完成后,将在ThreadPool 线程上执行该方法。 回调方法调用EndInvoke。

 

 

如何创建一个线程.

1.new Thread(new ThreadStart(methodA))

2.DelegationInstand.BeginInvoke()

3.ThreadPool.QueueworkItem()

 

Thread.IsBackground()

 

 

多线程是实现异步的手段和工具.  但并不是唯一手段,   如异步IO, 是由OS提供支持的?.  存疑, 又有说法: .NET线程池内线程分为工作和IO线程.   后者专用与异步IO.

 

Winform中, 将后台线程中UI组件调用送回UI线程.  Control.InvokeRequired 可以判断当前线程是否为UI线程.

1, Control.Invoke|BeginInvoke(); 

这两个方法是将参数方法送至控件创建的线程上执行, 也就是UI线程.底层调用Post/SendMessage

2,SynchronizationObject

 

var source = Enumerable.Range(0, 100000).ToArray();

 

 

任务并行库 (TPL) 是 .NET Framework 4 版的System.Threading 和 System.Threading.Tasks 命名空间中的一组公共类型和 API。

 

数据并行是指对源集合或数组中的元素同时(即并行)执行相同操作的情况。System.Threading.Tasks.Parallel 类中 For 和ForEach 方法的若干重载支持使用强制性语法的数据并行。

 

Partitioner 类提供 Create 方法,该方法使您可以为委托体提供顺序循环,以便每个分区只调用一次委托,而不是每个迭代调用一次委托

 

编写具有线程本地变量的 for, foreach 将并行处理结果合并.

停止或中断 Parallel.For 循环 内部

取消 Parallel.For 或 ForEach Loop 外部

 

任务并行(任务并行库), 对操作(而不是数据)进行并行化。

 

Parallel.Invoke(() => DoSomeWork(), () =>DoSomeOtherWork());

 

Task,,,, TaskCreateOptions,Task的取消

 

// Create a task and supply a user delegate by using alambda expression.

 var taskA = newTask(() => Console.WriteLine("Hello from taskA."));

 

// Start the task.

 taskA.Start();

 

// Create and start the task in one operation.

var taskA = Task.Factory.StartNew(() =>Console.WriteLine("Hello from taskA."));

 

使用 Task.ContinueWith 方法和Task<TResult>.ContinueWith 方法,可以指定在前面的任务完成时要启动的任务。

 

.NET Framework 提供以下两种执行 I/O 绑定和计算绑定异步操作的标准模式:

 

异步编程模型 (APM),在该模型中异步操作由一对 Begin/End 方法(如FileStream.BeginRead 和 Stream.EndRead)表示。

 

基于事件的异步模式 (EAP),在该模式中异步操作由名为 "操作名称Async"和"操作名称Completed"的方法/事件对(例如WebClient.DownloadStringAsync 和 WebClient.DownloadStringCompleted)表示

 

string User = SystemInformation.UserName;

string Domain = SystemInformation.UserDomainName;

 

 

myCodecs = ImageCodecInfo.GetImageEncoders();

myCodecs = ImageCodecInfo.GetImageDecoders();

 

Bitmap bmp1 = new Bitmap(typeof(Button),"Button.bmp");

bmp1.Save(@"c:\button.png", ImageFormat.Png);

 

iconForFile = Icon.ExtractAssociatedIcon(file.FullName);

 

 

[System.Runtime.InteropServices.DllImport("kernel32",SetLastError = true, ThrowOnUnmappableChar = true, CharSet =System.Runtime.InteropServices.CharSet.Unicode)]

 

 

Application.DoEvents();//vt处理事件.

 

Application.Run()启动消息循环

 

Dispatcher.Run()  WPF 启动消息循环,  会被Application.Run内部调用. 一般不用显式调用.

 

Application.Exit() 向所有窗口发送退出消息循环指令,  销毁窗口.  这并不一定意味着程序退出,只是UI线程退出.  只要有1个非后台线程,  .Net程序就不会退出.  除非显式调用System.exit

 

AppDomain..::.AssemblyResolve 事件 在对程序集的解析失败时发生。

 

Windows Message Processing

 

ThreadN Post/SendMessage to Message Queue/  Message Pump in owner thread disapatchmessage to corresponding window procedure.

 

Message Pump:

while(GetMessage()){

                TranslateMessage();

                DispatchMessage();

}

 

COM借用了Windows的消息机制, CoInitializeEx即创建了个隐藏的窗口,  这样当Com对象被外部调用时,就可以发消息到原线程,而不是直接访问对象了.

 

WaitForSingleObject会破坏程序中的消息机制, 窗口程序中等待线程需要用MsgWaitForMultipleObjects.COM隐式使用窗口所以也需要使用后者

 

Apartment是COM帮助减少线程同步复杂性的手段.  一个进程可以有多个STA,但只有一个MTA.

 

客户程序的线程模式只有两种,单线程套件(STA)和多线程套件(MTA)。

客户程序的线程是在调用CoInitializeEx()时决定客户线程的类型的。COINIT_APARTMENTTHREADED/COINIT_MULTITHREADED.  线程创建Apartment或加入Apartment.

 

 

组件所支持的模式有四种:Single(单线程)、Apartment(STA)、Free(MTA)、Both(STA+MTA)。

Single套间就是说每个被标志为Single的接口(COM对象),第一个以COINIT_APARTMENTTHREADED调用CoInitializeEx()的线程被称作是主STA。每次用CoCreateInstance()创建的Single型组件实际上都是创建在了这个主STA中,而不管是谁调用了CoCreateInstance()这个函数。所有对这个Single组件方法的调用都必须要通过这个主STA。

STA套间,  即当创建好的Com引用在多个线程中共享时,  方法调用是顺序的.  Single则是即使创建多次也实际是一个引用(主STA).   每个STA线程对应一个STA套件. 在不同Apartment间传递接口指针必须要经过调度,CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream,跨线程调用对象,套间提供同步保护.

MTA 即当创建好的Com引用在多个线程中共享时,  方法调用是并发的.

 

若STA创建STA型组件,是直接创建,直接调用。

若STA创建MTA型组件,系统为组件创建一个MTA,STA通过代理访问组件。

若STA创建Both型组件,是直接创建,直接调用。

若MTA创建STA型组件,系统为组件创建一个STA,MTA通过代理访问组件。

若MTA创建MTA型组件,是直接创建,直接调用。

若MTA创建Both型组件,是直接创建,直接调用。可见如果客户程序和组件都支持同样的线程模式,那么COM就允许客户程序直接调用对象,这样将产生最佳性能。


“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

donghong82

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值