题
如果编译器可以推断出泛型类型参数,C# 并不要求您指定它,例如:
List<int> myInts = new List<int> {0,1,1,
2,3,5,8,13,21,34,55,89,144,233,377,
610,987,1597,2584,4181,6765};
//this statement is clunky
List<string> myStrings = myInts.
Select<int,string>( i => i.ToString() ).
ToList<string>();
//the type is inferred from the lambda expression
//the compiler knows that it's taking an int and
//returning a string
List<string> myStrings = myInts.
Select( i => i.ToString() ).
ToList();
这对于您不知道类型参数是什么的匿名类型是必需的(在智能感知中它显示为 'a
)因为它是由编译器添加的。
类级别类型参数不允许您执行此操作:
//sample generic class
public class GenericDemo<T>
{
public GenericDemo ( T value )
{
GenericTypedProperty = value;
}
public T GenericTypedProperty {get; set;}
}
//why can't I do:
int anIntValue = 4181;
var item = new GenericDemo( anIntValue ); //type inference fails
//however I can create a wrapper like this:
public static GenericDemo<T> Create<T> ( T value )
{
return new GenericDemo<T> ( value );
}
//then this works - type inference on the method compiles
var item = Create( anIntValue );
为什么 C# 不支持此类级别的泛型类型推断?
解决方案
其实你这个问题问的也不错。过去几年我一直在研究通用编程语言,尽管我从未真正开发过它(可能永远不会),但我对通用类型推断思考了很多,我的首要任务之一是始终允许构造类而不必指定泛型类型。
C# 只是缺乏一套使这成为可能的规则。我认为开发人员从未意识到有必要包含这一点。实际上,下面的代码将非常接近您的命题并解决问题。C# 所需要的只是添加语法支持。
class Foo<T> {
public Foo(T x) { … }
}
// Notice: non-generic class overload. Possible in C#!
class Foo {
public static Foo<T> ctor<T>(T x) { return new Foo<T>(x); }
}
var x = Foo.ctor(42);
由于这段代码确实有效,我们已经证明问题不在于语义,而在于缺乏支持。我想我必须收回我之前的帖子。;-)
其他提示
为什么 C# 不支持此类级别的泛型类型推断?
因为它们一般都是含糊不清的。相比之下,类型推断对于函数调用来说是微不足道的(如果所有类型都出现在参数中)。但在构造函数调用的情况下(为了讨论而美化函数),编译器必须同时解析多个级别。一个级别是类级别,另一个级别是构造函数参数级别。我相信解决这个问题在算法上并不简单。直觉上,我会说它甚至是 NP 完全的。
为了说明不可能解析的极端情况,想象一下下面的类并告诉我编译器应该做什么:
class Foo<T> {
public Foo<U>(U x) { }
}
var x = new Foo(1);
谢谢康拉德,这是一个很好的回应(+1),但只是为了扩展它。
让我们假设 C# 有一个显式的构造函数:
//your example
var x = new Foo( 1 );
//becomes
var x = Foo.ctor( 1 );
//your problem is valid because this would be
var x = Foo<T>.ctor<int>( 1 );
//and T can't be inferred
您说得对,无法推断第一个构造函数。
现在让我们回到课堂
class Foo<T>
{
//<T> can't mean anything else in this context
public Foo(T x) { }
}
//this would now throw an exception unless the
//typeparam matches the parameter
var x = Foo<int>.ctor( 1 );
//so why wouldn't this work?
var x = Foo.ctor( 1 );
当然,如果我将构造函数添加回来(及其替代类型),我们就会有一个不明确的调用 - 就像无法解析正常的方法重载一样。
不隶属于 StackOverflow