关于.net:为什么C#不支持在类构造函数中隐含的泛型类型?

关于.net:为什么C#不支持在类构造函数中隐含的泛型类型?

Why doesn't C# support implied generic types on class constructors?

如果编译器可以推断出C#,则不需要您指定通用类型参数,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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),因为它是由编译器添加的。

类级别的类型参数不允许您执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//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#需求都是一种附加的语法支持。

1
2
3
4
5
6
7
8
9
10
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);

由于该代码实际上有效,因此我们证明了问题不只是语义之一,而仅仅是缺乏支持之一。我想我必须收回以前的帖子。 ;-)


Why doesn't C# support this class level generic type inference?

因为它们通常是模棱两可的。相比之下,对于函数调用,类型推断是微不足道的(如果所有类型都出现在参数中)。但是,对于构造函数调用(为便于讨论,使用通用函数),编译器必须同时解析多个级别。一个级别是类级别,另一个级别是构造函数参数级别。我相信解决这个问题在算法上并不简单。凭直觉,我想说它甚至是NP完整的。

为了说明无法解决的极端情况,请想象以下类,并告诉我编译器应该做什么:

1
2
3
4
5
class Foo< T > {
    public Foo<U>(U x) { }
}

var x = new Foo(1);


感谢Konrad,这是一个不错的答复(+1),但只是在此基础上进行扩展。

假设C#具有显式的构造函数:

1
2
3
4
5
6
7
8
9
//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

您完全正确,无法推断出第一个构造函数。

现在回到课上

1
2
3
4
5
6
7
8
9
10
11
12
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 );

当然,如果我重新添加构造函数(及其替代类型),则会产生一个模棱两可的调用-就像无法解决常规方法重载一样。


推荐阅读