在回顾github上的一些代码时,我遇到了这种模式:
using System.Linq.Expressions;
public T SomeGenericMethod<TValue>(Expression<Func<T, TValue>> myExpr){
// ...
}
这是代码中唯一相关的部分。 TValue
从未在方法体中实际使用过,它仅用于Func<,>
类型。
它的用法如下:
myObj.SomeGenericMethod(x => x.SomeProperty)
请注意,对SomeGenericMethod
的调用不会传递通用。我原以为编译器需要这样的东西:
myObj.SomeGenericMethod<SomeTValue>(x => x.SomeProperty)
但它没有。
所以我的问题是,什么是TValue
什么时候没有显式传递作为泛型方法调用的类型?
答案 0 :(得分:3)
在这种情况下,它是typeof(SomeProperty)
。 C#编译器将自动发现它。来自https://msdn.microsoft.com/en-us/library/twcad0zb.aspx
您也可以省略type参数,编译器会推断它。
编译器可以根据您传入的方法参数推断类型参数;它不能仅从约束或返回值推断类型参数。因此,类型推断不适用于没有参数的方法。类型推断在编译器尝试解析重载的方法签名之前的编译时发生。编译器将类型推断逻辑应用于共享相同名称的所有通用方法。在重载解析步骤中,编译器仅包括那些类型推断成功的泛型方法。
请注意,您可以认为这种用法很奇怪,但是当您使用LINQ时,您可以正常使用它。用Line编写:
var coll = new[] { new { Foo = 1, Bar = 2 } };
var enu = coll.Select(x => x.Foo).ToList();
您没有明确说明Foo
中Select
的任何类型。编译器扣除它是int
。
Select
的签名是:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
因此Foo
的类型是TResult
如果没有这种类型的推断,匿名对象几乎是无用的,因为你不能:
class MyClass
{
public int Foo { get; set; }
public int Bar { get; set; }
}
var coll = new[] { new MyClass { Foo = 1, Bar = 2 } };
var enu = coll.Select<MyClass, ???>(x => new { Bar = x.Foo }).ToList();
你会在???
中加入什么?根据匿名对象的定义,您无法明确命名它们: - )
答案 1 :(得分:1)
当编译器可以执行泛型推理时,你可以不明确地传递泛型,其中它将使用传递的参数类型来确定要调用的泛型版本。请注意,推理在编译时发生,因此可能会对多态性产生一些奇怪的副作用。
为了支持T
值,此方法需要在class
范围内,其T
作为参数。因为类本身每个参数编译一次,所以任何组成成员(包括子类!)都可以使用该泛型参数,这包括将它用于参数的参数,或者进一步通用调用的泛型参数。