Почему поддержка С# не подразумевает общие типы в конструкторах классов?

С# не требует указания параметра общего типа, если компилятор может его вывести, например:

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

Почему С# не поддерживает этот тип общего вывода типа класса?

Ответы

Ответ 1

Собственно, ваш вопрос не плох. Я занимаюсь общим языком программирования за последние несколько лет, и хотя я никогда не приходил к нему, чтобы на самом деле его развить (и, вероятно, никогда не будет), я много думал о типичном типе, и один из моих главных приоритетов всегда заключалось в том, чтобы разрешить построение классов без указания общего типа.

С# просто не хватает набора правил, чтобы сделать это возможным. Я думаю, что разработчики никогда не видели необходимости включать это. Фактически, следующий код будет очень близок к вашему предложению и решить проблему. Все потребности С# - это дополнительная поддержка синтаксиса.

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

Поскольку этот код действительно работает, мы показали, что проблема не в семантике, а просто в отсутствии поддержки. Думаю, мне нужно отменить предыдущую публикацию.; -)

Ответ 2

Почему С# не поддерживает этот тип общего вывода типа класса?

Потому что они обычно неоднозначны. Напротив, вывод типа является тривиальным для вызовов функций (если все типы появляются в аргументах). Но в случае вызовов конструктора (прославленные функции, для обсуждения) компилятор должен разрешать несколько уровней одновременно. Один уровень - это уровень класса, а другой - уровень аргументов конструктора. Я считаю, что это решение алгоритмически нетривиально. Интуитивно я бы сказал, что это даже NP-complete.

Чтобы проиллюстрировать крайний случай, когда разрешение невозможно, представьте себе следующий класс и скажите мне, что должен сделать компилятор:

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

var x = new Foo(1);

Ответ 3

Спасибо Konrad, что хороший ответ (+1), но просто для его расширения.

Предположим, что С# имеет явную конструкторскую функцию:

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

Конечно, если я добавлю свой конструктор обратно (с его альтернативным типом), мы получим двусмысленный вызов - точно так же, как если бы нормальная перегрузка метода не могла быть разрешена.