为什么C#不支持类构造函数中隐含的泛型类型?

时间:2008-09-05 12:01:39

标签: c# .net generics

如果编译器可以推断它,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();

这是匿名类型所必需的,你不知道类型参数是什么(在intellisense中显示为'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#不支持此类级泛型类型推断?

3 个答案:

答案 0 :(得分:28)

实际上,你的问题还不错。在过去的几年里,我一直在玩一种通用的编程语言,尽管我从来没有真正开发它(也许永远不会),我已经考虑了很多关于泛型类型的推理,我的首要任务之一是总是允许构造类而不必指定泛型类型。

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);

由于这段代码确实有效,我们已经证明问题不是语义问题,而是缺乏支持问题。我想我必须收回以前的帖子。 ; - )

答案 1 :(得分:11)

  

为什么C#不支持此类级泛型类型推断?

因为它们通常含糊不清。相比之下,类型推断对于函数调用来说是微不足道的(如果所有类型都出现在参数中)。但是在构造函数调用(美化函数,为了讨论)的情况下,编译器必须同时解析多个级别。一个级别是类级别,另一个级别是构造函数参数级别。我相信解决这个问题在算法上是非平凡的。直观地说,我认为它甚至是NP完全的。

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

class Foo<T> {
    public Foo<U>(U x) { }
}

var x = new Foo(1);

答案 2 :(得分:2)

感谢Konrad,这是一个很好的回应(+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 );

当然,如果我重新添加你的构造函数(使用其替代类型),我们会有一个模糊的调用 - 就像无法解决正常的方法重载一样。