当泛型方法推断,扩展方法遇到泛型类型in/out时。。。
2014-05-19 22:39
519 查看
说到泛型方法,这个是.net 2.0的时候引入的一个重要功能,c#2.0也对此作了非常好的支持,可以不需要显试的声明泛型类型,让编译器自动推断,例如:
此时,编译器可以自动推导出这里的T就是int,这极大的方便了我们写代码的效率。
说到扩展方法,这个是.net 3.5的时候引入的另一个重要功能,c#3.0也在linq中大量的应用这个功能,当扩展方法是扩展一个泛型的类型时,显然也不需要我们指定具体的泛型类型,编译器会为我们自动推断,例如:
最后说到协变和逆变(也就是c#中的in/out),这个是.net 4.0的时候引入的一个重要的功能,例如:
然后,我们将泛型方法推断和协变和逆变放在一起:
看起来很不错,不过要是遇到些复杂点的会怎么样?
看到这里相信所有都为c#的in/out拍手叫好,不过别急,除了out+out我们还可以玩令人抓狂的in+in:
看到这里有没有发现什么问题?如果你没觉得有什么不舒服的感觉,说明你一定是懂协变和逆变的高手或是完全不懂的初学者。
先想下定义:Action<in T>,T 是in的,也就是Action<object>里面的object可以被string这样更具体的类型替代,而这里Action<Action<string>>里面的Action<string>被Action<object>替代了,怎么看都感觉有些怪异,不过在细细品味一下,就可以发现这个结果是完全合理的。string虽然比object更具体,不过一个接受string的方法可比一个接受object的方法更抽象,所以可以简单的得到一个结论:in+in=>out
文章要是到这里结束,估计很多人就认为本文是对c#的无比赞美了吧,不过,重点是这里,别忘了,多个泛型参数可以玩出很多猥琐的东西,例如,双/多泛型锁定(随便起的名字):
c#编译器对扩展方法支持的确是有一手,这么变态的T也可以被推断出是object,不得不佩服一把,再来看看out的情况(别忘了前面的结论in+in=>out):
c#编译器依然表现出专业的结果,正确的推断出了T应当是string,不过,泛型方法的类型推断却完全是另外一番风景:
看到这个结果是不是想大骂c#编译器:这也太山寨了吧。
别急,我们还可以玩得更加浮云:
或者这个:
c#编译器显然不想瞎猜T的类型是什么,要求必须编程者明确指出T的具体类型。
void F<T>(T value){} //... int i = 0; F(i);
此时,编译器可以自动推导出这里的T就是int,这极大的方便了我们写代码的效率。
说到扩展方法,这个是.net 3.5的时候引入的另一个重要功能,c#3.0也在linq中大量的应用这个功能,当扩展方法是扩展一个泛型的类型时,显然也不需要我们指定具体的泛型类型,编译器会为我们自动推断,例如:
static void F<T>(this List<T> list){} //... List<int> list = new List<int>(); list.F();
最后说到协变和逆变(也就是c#中的in/out),这个是.net 4.0的时候引入的一个重要的功能,例如:
Func<string> foo = () => "Foo"; Func<object> bar = foo;
然后,我们将泛型方法推断和协变和逆变放在一起:
public static void Foo(this Action<string> action){} //... Action<object> action = o => {}; action.Foo();
看起来很不错,不过要是遇到些复杂点的会怎么样?
public static void Foo(this IEnumerable<IEnumerable<object>> that) {} //... List<List<string>> bar = new List<List<string>>(); bar.Foo();
看到这里相信所有都为c#的in/out拍手叫好,不过别急,除了out+out我们还可以玩令人抓狂的in+in:
public static void Foo(this Action<Action<object>> that) {} //... Action<Action<string>> action = a => a("O_O"); action.Foo();
看到这里有没有发现什么问题?如果你没觉得有什么不舒服的感觉,说明你一定是懂协变和逆变的高手或是完全不懂的初学者。
先想下定义:Action<in T>,T 是in的,也就是Action<object>里面的object可以被string这样更具体的类型替代,而这里Action<Action<string>>里面的Action<string>被Action<object>替代了,怎么看都感觉有些怪异,不过在细细品味一下,就可以发现这个结果是完全合理的。string虽然比object更具体,不过一个接受string的方法可比一个接受object的方法更抽象,所以可以简单的得到一个结论:in+in=>out
文章要是到这里结束,估计很多人就认为本文是对c#的无比赞美了吧,不过,重点是这里,别忘了,多个泛型参数可以玩出很多猥琐的东西,例如,双/多泛型锁定(随便起的名字):
public static void Foo<T>(this Action<T, T> that) {} //... Action<string, object> action = (s, o) => {}; action.Foo();
c#编译器对扩展方法支持的确是有一手,这么变态的T也可以被推断出是object,不得不佩服一把,再来看看out的情况(别忘了前面的结论in+in=>out):
public static void Foo<T>(this Action<Action<T>, Action<T>> that) {} //... Action<Action<string, object>> action = (s, o) => {}; action.Foo();
c#编译器依然表现出专业的结果,正确的推断出了T应当是string,不过,泛型方法的类型推断却完全是另外一番风景:
public void Foo<T>(Action<T, T> that) {} //... Action<string, object> action = (s, o) => {}; Foo(action); // failed. Foo<object>(action); // failed. Foo((Action<object, object>)action); // succeeded.
看到这个结果是不是想大骂c#编译器:这也太山寨了吧。
别急,我们还可以玩得更加浮云:
public static Foo<T>(this Action<Action<T>, Action<T>> that) {} // ... Action<Action<List<string>>, Action<ArrayList>> bar = null; bar.Foo(); // failed. bar.Foo<IEnumerable>(); // succeeded.
或者这个:
public static Foo<T>(this Action<T, T> that) {} // ... Action<IEnumerable<char>, IComparable<string>> action = null; action.Foo(); // failed. action.Foo<string>(); // succeeded.
c#编译器显然不想瞎猜T的类型是什么,要求必须编程者明确指出T的具体类型。
相关文章推荐
- 复习扩展方法 涉及委托,这里我使用自定义委托类型 public delegate bb MyFunc<in T,out bb> (T arg)
- c# in depth 之泛型实参的类型推断
- 使用Gson解析Json数组遇到的泛型类型擦除问题解决方法
- C#编译器对泛型方法调用作类型推断的奇怪问题
- 泛型(Generic):了解泛型、泛型的应用、?通配符和泛型的限定、泛型方法和类型推断、泛型类、用反射获得实际
- C#编译器对泛型方法调用作类型推断的奇怪问题
- 泛型方法与类型推断
- java泛型学习-自定义泛型方法与类型推断总结
- Java泛型方法定义及泛型类型推断
- Type对象获得泛型类型的两个扩展方法
- 41_自定义泛型方法的练习与类型推断总结
- Java泛型方法定义及泛型类型推断
- java泛型-自定义泛型方法与类型推断总结
- Java泛型方法定义及泛型类型推断
- Java泛型方法定义及泛型类型推断
- Java8中对泛型目标类型推断方法的改进
- 自定义泛型方法及其应用和类型参数的类型推断
- C#拾遗系列(9):继承、接口、扩展方法、分部类、类操作、Ref and Out、可空类型
- 自定义泛型方法的练习与类型推断总结
- 利用反射,泛型,扩展方法快速获取表单值到实体类