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

StackOverflow https://stackoverflow.com/questions/45604

  •  09-06-2019
  •  | 
  •  

Вопрос

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# не поддерживает вывод универсального типа на уровне класса?

Это было полезно?

Решение

На самом деле ваш вопрос неплох.Последние несколько лет я экспериментировал с обобщенным языком программирования, и хотя мне никогда не доводилось его разрабатывать (и, вероятно, никогда не займусь), я много думал о выводе обобщенного типа, и одним из моих главных приоритетов является всегда было разрешено создание классов без указания универсального типа.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top