技术开发 频道

C# 4.0新特性

  省略ref

  由于采用了不同的编程模型,很多COM API包含大量的引用参数。与C#中的ref相反,这些参数并不意味着要修改传入的实参以供调用方之后使用,而只是另外一种传递参数值的简单方式。

  C#程序员必须为所有这些ref参数创建临时变量,并按引用进行传递,这看上去一点也不合理。因此,对于COM方法,C#编译器允许按值传递这些参数,并自动生成存放传入值的临时变量,并在调用返回后丢弃这些变量。使用这种方式,调用方看到的语义是按值传递,而且不会有任何副作用,而被调用的方法得到的依然是一个引用。

  已知问题

  一小部分COM接口特性没有出现在C#中。尤其是索引属性和默认属性。如果是动态访问COM,可以用之前提到的特性来解决,但静态类型的C#代码仍然无法识别它们。

  在C# 4.0中没有计划解决这些剩余的速度损失。

  变性

  泛型的某个方面会让人感到奇怪,比如下面的代码是不合法的——

IList<string> strings = new List<string>();  
IList
<object> objects = strings;  

  第二个赋值是不允许的,因为stringsobjects的元素类型并不一样。这样做有这充分的原因。如果允许那样写的话,你可能会写——

objects[0] = 5;  
string s = strings[0];  

  这会允许将int插入strings列表中,然后将其作为string取出。这会破坏类型安全。

  然而,对于某些接口来说上述情况并不会发生,尤其是不能将对象插入集合时。例如IEnumerable<T>就是这样的接口。如果改为——

IEnumerable<object> objects = strings;  

 

 

  这样就没法通过objects将错误类型的东西插入到strings中了,因为objects没有插入元素的方法。变性(variance)就是用于在这种能保证安全的情况下进行赋值的。结果就是很多之前让我们感到奇怪的情况现在可以工作了。

  协变性

  在.NET 4.0中,IEnumerable<T>接口将会按照下面的方式进行定义——

public interface IEnumerable<out T> : IEnumerable  
{  
        IEnumerator
<T> GetEnumerator();  
}  
  
public interface IEnumerator<out T> : IEnumerator  
{  
        
bool MoveNext();  
        T Current {
get; }  
}  

 

 
  这些声明中的“out”指出T只能出现在接口的输出位置——如果不是这样的话,编译器会报错。有了这一限制,接口对于T类型就是“协变的”,这意味着如果A可以按引用转换为B,则IEnumerable<A>可以当作IEnumerable<B>使用。

  其结果是,任何一个字符串序列也就是一个对象序列了。

  这很有用,例如在LINQ方法中。使用上面的定义——

var result = strings.Union(objects); // succeeds with an IEnumerable<object>  

 

 

  之前这样做是不允许的,你必须做一些麻烦的包装,使得两个序列具有相同的元素类型。

  逆变性

  类型参数还可以具有“in”修饰符,限制它们只能出现在输入位置上。例如IComparer<T>

public interface IComparer<in T>  
{  
        
public int Compare(T left, T right);  
}  
 

  其结果有点让人迷惑,就是IComparer<object>可以作为IComparer<string>使用!这样考虑这个结果就会很有意义——如果一个比较器可以比较任意两个object,它当然也可以比较两个string。这种性质被称作“逆变性(contravariance)”。

  泛型类型可以同时拥有带有inout修饰符的类型参数,例如Func<...>委托类型——

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

 

 

  很明显参数永远都是传的,而结果永远只能是传的。因此,Func<object, string>可以用作Func<string, object>

0
相关文章