C#学习笔记1:常见问题

概念

同步、异步、阻塞、非阻塞

转自知乎-严肃的回答

“阻塞”与”非阻塞”与”同步”与“异步”不能简单的从字面理解,提供一个从分布式系统角度的回答。

  • 同步与异步

同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。
换句话说,就是由调用者主动等待这个调用的结果。

而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

典型的异步编程模型比如Node.js,举个例子:

普通B/S模式(同步)AJAX技术(异步)

同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事

异步: 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕

举个通俗的例子:

你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

  • 阻塞与非阻塞

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

还是上面的例子,

你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

语法

int? 的意思

下面两种声明int变量的方式:

int num
int? num

区别在于,第二种中的num可以为空(或者赋值为null),而第一种只能赋值整数类型值。

对于其他的基础类型也同样适用。

关键字

ref与out的区别

C#中参数传递的方式有两种,分别是值传递和引用传递。refout这两种方式都是属于引用传递。唯一的区别在于:

  • out关键字修饰的参数需要在调用的函数内部先初始化,再使用
  • ref关键字修饰的参数需要在调用函数前,先初始化再传入函数

static readonly与const的区别

static readonlyconst非常类似,共同点有:(1) 通过类名而不是对象名进行访问;(2) 在程序中只读(不可更改)等。两者的本质区别在于:

  • const的值在编译期间就是确定的,只能在声明时通过常量表达式指定值;
  • static readonly是在运行时计算出其值的,所以还可以通过静态构造函数来赋值。

下面的例子可以更加清楚地认识static readonlyconst的区别:

1. static readonly MyClass myins = new MyClass();
2. static readonly MyClass myins = null;
3. static readonly B = 10;   static readonly A = B * 20;
4. static readonly int [] constIntArray = new int[] {1, 2, 3};
5. void SomeFunction()
   {
      const int a = 10;
      ...
   }
6.private static string astr="abcd";
  private const string str = astr+"efg";

1:不可以 换成const。new操作符是需要执行构造函数的,所以无法在编译期间确定;

2:可以换成const。我们也看到,Reference类型的常量 (除了String)只能是Null;

3:可以换成const。我们可以在编译期间很明确的说,A等于200;

4:不可以换成 const。道理和1是一样的,虽然看起来1,2,3的数组的确就是一个常量;

5:不可以换成readonly,readonly只能用来修饰类 的field,不能修饰局部变量,也不能修饰property等其他类成员;

6.错误:如果在astr前加上const或者const改为readonly即可;

总结如下

  • const、readonly和static readonly定义的常量,指定初始值后(包括在构造函数内指定的初始值) 将不可更改,可读不可写;
  • const定义时必须指定初始值,而readonly定义时可以不进行初始化(MS建议在定义时初始值),同时也可以在构造函数内指定初始值,并以构造函数内指定的值为准;
  • const和static readonly定义的常量是静态的,只能由类直接访问;而readonly定义的常量是非静态的,只能由实例对象访问;
  • static readonly常量,如果在构造函数内指定初始值,则必须是静态无参构造函数;
  • const是编译时常量,readonly是运行时常量;cosnt较高效,readonly较灵活。在应用上以static readonly代替const,以平衡const在灵活性上的不足,同时克服编译器优化cosnt性能,所带来的程序集引用不一致问题;

static关键字

使用static修饰符声明属于类型本身而不是属于特定对象的静态成员。static修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。

  • static修饰的类称为静态类,静态类与非静态类的区别在于:静态类不能够被实例化(不能包含构造函数),仅包含静态成员,访问成员的方式是通过类名
  • 静态成员的初始化工作可以在静态构造函数内部完成(包括static readonly修饰符修饰的成员);

static修饰符相对应的是auto,通常它是默认的(即不用static修饰的都是auto的)。auto的含义是由程序自动控制变量的生存周期,通常指的就是变量在进入其作用域的时候被分配,离开其作用域的时候被释放;而static就是不auto,变量在程序初始化时被分配,直到程序退出前才被释放;也就是static是按照程序的生命周期来分配释放变量的,而不是变量自己的生命周期。看下面的例子:

void func()
{
     int a;
     static int b;
}

每一次调用上面的func函数,变量a都是新的,因为它是在进入函数体的时候被分配,退出函数体的时候被释放,所以多个线程调用该函数,都会拥有各自独立的变量a,因为它总是要被重新分配的;而变量b不管你是否使用该函数,在程序初始化时就被分配的了,或者在第一次执行到它的声明的时候分配(不同的编译器可能不同),所以多个线程调用该函数的时候,总是访问同一个变量b,这也是在多线程编程中必须注意的。

语句

使用using block

在进行文件的读写操作、数据库连接操作等涉及程序结束时资源的释放,通常建议使用using block。格式如下:

using(代码段)
{
    代码段...
}

好处在于using block结束后,using里面声明的对象会自动释放内存。尽管.Net的垃圾处理机制会处理,但那是不可控制的。

下面是一个使用using block进行文件的读操作的示例,可以看出使用using block能够让代码更加简洁。

using (StreamReader sr=new .....) 
{} 

相当于

StreamReader sr=null; 
try 
{ 
    sr=new ...; 
} 
finally 
{ 
    sr.Dispose(); 
}

使用LINQ语句

多种数据源(如数据库、XML、数组、集合等)的数据格式是不一样的,因此常常需要对不同的数据格式进行相互转换,以及学习多种数据查询语言。为了使得数据查询和操作变得更加简便,.NET 3.5版本发布了LINQ。

LINQ全称为语言集成查询(Language Integrated Query),是一种用来进行数据访问的编程模型,它是C# 3.0和.Net 3.5中的主要新增功能。LINQ以统一的方式操作各种数据源,降低数据访问的复杂度。LINQ支持的数据源有SQL Sever、XML以及内存中的数据集合。

下面举一个简单的示例,以说明使用LINQ语句带来的简便性:

bool flag = false;
foreach (var elem in list)
{
    if (elem...)
    {
        flag = true;
        break;        
    }
}
...

使用LINQ语句就是

bool flag = list.Any(elem => ...)

可以看到使用LINQ可以使得代码变得非常简洁。

OO相关

C#中的field与property

property与field的区别在于,property拥有访问器,访问器定义了读取或者写入属性值必须执行的代码。property不是变量,没有存储数据的功能,数据都存在字段中,因此不能对property直接赋值,必须通过set访问器。

为了类的封装性,一般将描述类的特征的字段定义为private,再将属性定义为public来操作私有字段。例如如下:

private string phoneNumber;
public string PhoneNumber
{
    get { return phoneNumbe; }
    set { phoneNumber = value; }
}

上述代码也可以简写为

public PhoneNumber { get; set; }

使用property最大的好处在于可以在对field进行赋值时,加入一些逻辑,例如限制只能给字段赋于某个范围的值、或是要求字段只能读或只能写等。


本文结束,感谢欣赏。

欢迎转载,请注明本文的链接地址:

http://www.jeyzhang.com/cshape-learning-notes-1.html