未明确通过时的通用类​​型的值?

时间:2015-04-20 14:01:26

标签: c# generics

在回顾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什么时候没有显式传递作为泛型方法调用的类型?

2 个答案:

答案 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();

您没有明确说明FooSelect的任何类型。编译器扣除它是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作为参数。因为类本身每个参数编译一次,所以任何组成成员(包括子类!)都可以使用该泛型参数,这包括将它用于参数的参数,或者进一步通用调用的泛型参数。