Learning CLR via C#(2)

Tips:

  • 可选参数和默认参数的配合:
static void Test()
{
    M(c: 5);
}

static void M(int a = 1, int b = 2, int c = 3)
{
    //do sth
}
这样可以前两个参数使用默认值。
  • 从CLR的角度看,out和ref完全一致。C#编译器将这两个关键字区别对待,这个区别决定了由哪个方法负责初始化所引用的对象。
  • 可以为索引器重命名,默认为Item。可通过属性System.Runtime.CompilerServices.IndexerNameAttribute重命名。 
  • 泛型方法,对于任何引用类型,都会使用相同的代码。因为本质上都是操作一个指针。
  • 逆变量参数只能出现在输入位置,比如作为方法的参数。协变量参数只能出现在输出位置,比如作为方法的返回类型。
IEnumerable<object> objs = new List<string>();

这个表达式看起来非常自然,可惜的是Framework4.0之前这样赋值是不可能的。

因为IEnumerable<out T>的泛型参数是一个协变量。仔细考虑下,objs的返回值需要一个object类型,但是我们传入的是一个能返回string类型的List,那么无疑,这个传入的值是有效的,因为string类型是object的子类。

 

相反,看如下的表达式:

Action<string> b = new Action<object>(o => Console.WriteLine(o.GetType()));

这个就比较违反我们的思维定势了,怎么能把一个object类型隐式转型赋值给一个string类型?

因为Action<in T>的泛型参数是一个逆变量。仔细想想,如果现在有一个委托只需要object类型的参数就可以工作,我们给它一个string当然没问题。

 

总结一下就是,协变量可以由他的派生类更改为他的基类,逆变量可以由他的基类更改为派生类。

public delegate TResult Func<in T, out TResult>(T arg);

看这个委托的定义,我们可以这样使用:

Func<string, Exception> fn2 = new Func<object, ArgumentException>((o) => null);

主要是看转换过程。

第一个泛型参数是逆变量,因为他从object更改成了string,即从基类更改为派生类。第二个是协变量,因为它从ArgumentException更改成Exception,即从派生类更改为基类。

  1. 类型参数可以指定零个或一个主要约束(引用类型、struct、class),零个或多个次要约束(接口、类型参数约束),零个或一个构造器约束(无参)。
  2. 将一个泛型参数的变量转型为另一个参数是非法的,除非将其转型为与一个约束兼容的类型。
private void Cast<T>(T generic)
{
    int a = (int)generic;
    string b = (string)generic;
}
上述代码会编译失败,若要编译成功,可以先转型为object
private void Cast<T>(T generic)
{
    int a = (int)(object)generic;
    string b = (string)(object)generic;
}
  1. default(T)告诉C#编译器和CLR的JIT编译器,如果T是一个引用类型,就将其设为null,如果是值类型,就将temp的所有位设为0.
  2. 无论泛型是否被约束,使用==或!=操作符将一个泛型类型变量与null进行比较都是合法的:
private void Cast<T>(T generic)
{
    if (generic == null)
    {
        //do sth
    }
}
posted @ 2011-05-02 23:06  我就是砖家  阅读(204)  评论(0编辑  收藏  举报