Вопрос по constructor, type-inference, generics, c# – Почему конструктор C # не может определить тип?

150

Почему вывод типов не поддерживается для конструкторов, как это делается для универсальных методов?

public class MyType<T>
{
   private readonly T field;
   public MyType(T value) { field = value; }
}

var obj = new MyType(42); // why can't type inference work out that I want a MyType<int>?

Хотя вы могли бы обойти это с классом фабрики,

public class MyTypeFactory
{
   public static MyType<T> Create<T>(T value)
   {
      return new MyType<T>(value);
   }
}
var myObj = MyTypeFactory.Create(42);

Есть ли практическая или философская причина, почему конструктор не может поддерживать вывод типа?

Если вы пытаетесь передать несколько классов для строго типизированного представления, попробуйте следующее: return View (Tuple.Create (new Person (), new Address ())); Marquinho Peli
Это правильный ответ на мой взгляд. Поскольку он единственный, который дает прагматичное решение. Решение, которое можно использовать в реальной жизни. Используя заводской шаблон. Еще лучше, если вы назовете свой завод так же, как ваш общий тип. Darkonekt
У меня был тот же вопрос за два года до этого:stackoverflow.com/questions/45604так что технически это дубликат. Ответ Эрика отличный и полный, хотя. Keith
Пожалуйста, проголосуйте за запрос функции!Предложение: вывод аргумента типа конструктора Colonel Panic

Ваш Ответ

5   ответов
12

Основная причина, по которой вывод универсального типа не может работать с конструкторами так, как вы хотите, заключается в том, что класс «MyType» даже не существует, когда все, что вы объявили, это «MyType <T>». Помните, что законно иметь оба:

public class MyType<T> {
}

а также

public class MyType {
}

Оба были бы законными. Как бы вы избавились от неоднозначности своего синтаксиса, если бы вы действительно объявили оба, и оба они объявили конфликтующий конструктор.

Msgstr "Как бы вы двусмысленно интерпретировали свой синтаксис". В этом случае вам нужно будет указать T. Pedro Henrique
18
public class MyType<T> 
{ 
   private readonly T field; 
   public MyType(T value) { field = value; } 
} 

они могут, нет необходимости снова сообщать конструктору «что такое T», поскольку вы уже сделали это в объявлении класса.

также ваш завод неверен, вам нужно иметьpublic class MyTypeFactory<T> не простоpublic class MyTypeFactory - если вы не объявите фабрику внутриMyType учебный класс

Изменить для обновления:

Ну, 42 это длинный, короткий, int или что-то еще?

Допустим, у вас есть следующее

class Base
{
   public virtual void DoStuff() { Console.WriteLine("Base"); }
}

class Foo : Base
{
   public override void DoStuff() { Console.WriteLine("Foo");  }
}

Тогда ты сделал это

var c = new Foo();

var myType = new MyType(c);

Вы ожидаетеfoo быть использованным, илиbase? Нам нужно указать компилятору, что использовать вместоT

Когда ты действительно хотел по типуbase

Следовательно

var myType = new MyType<Base>(c);
@theburningmonk не может, если MyTypeFactory не объявлен внутри класса MyType 'Не удается разрешить символ' T '' PostMan
@PostMan - глупый мне, это должно было прочитать Create <T>! theburningmonk
@PostMan: часть вашего ответа, с которой я не согласен, состоит в том, что <quote> ваш завод неверен, вам нужно иметьpublic class MyTypeFactory<T> не простоpublic class MyTypeFactory - если вы не объявите фабрику внутриMyType класс </ quote> На самом деле, правильная вещьMyTypeFactory.Create<T> как сейчас вопрос есть, а неMyTypeFactory<T>.Create который ты сказал, был нужен. С помощьюMyTypeFactory<T>.Create предотвратит вывод типа, поскольку универсальный параметр появляется перед оператором ссылки на член. не может быть выведено, тот, который на метод может. Ben Voigt
@Ben Voigt - я так не говорил, изначально в операторе была ошибка на его заводе, я просто исправил ее PostMan
119

Есть ли философская причина, почему конструктор не может поддерживать вывод типа?

Нет, когда у вас есть

new Foo(bar)

затем мы могли бы идентифицировать все типы, называемые Foo, в области видимости независимо от общей арности, а затем выполнить разрешение перегрузки для каждого, используя модифицированный алгоритм вывода типа метода. Затем нам нужно создать алгоритм «лучшего поведения», который определяет, какой из двух применимых конструкторовв двух типах, которые имеют одинаковое имя, но разные общие арности лучший конструктор. Чтобы поддерживать обратную совместимость, ctor на неуниверсальном типе должен всегда побеждать.

Есть ли практическая причина, почему конструктор не может поддерживать вывод типа?

Да. Даже если выгода от этой функции превышает ее стоимость, которая является значительной, этого недостаточно для реализации этой функции. Эта функция должна быть не только чистой победой, но ибольшой Чистая победа по сравнению со всеми другими возможными функциями, в которые мы могли бы инвестировать. Это также должно быть лучше, чем тратить это время и усилия на исправление ошибок, работу с производительностью и другие возможные области, в которые мы могли бы вложить эти усилия. И в идеале он должен хорошо вписываться в «тему» ​​релиза.

Кроме того, как вы правильно заметили, вы можете получить преимущества этой функции, фактически не имея самой функции, используя фабричный шаблон. Наличие простых обходных путей снижает вероятность того, что функция когда-либо будет реализована.

Эта функция уже давно включена в список возможных. Это никогда не было достаточно высоко в списке, чтобы быть реализованным.

ОБНОВЛЕНИЕ март 2015

Предложенная особенность сделала его достаточно близким к вершине списка для C # 6, который будет указан и разработан, но затем был сокращен.

Интересно, что через пару лет это сейчас в работе для Roslyn. Magus
Я не собираюсь переключать языки, и я не знаю, почему вы говорите, что это не цель C #, потому что, насколько я могу видеть, компилятор C # уже делает вывод типов практически во всех сценариях, ЗА ИСКЛЮЧЕНИЕМ случая конструктора , Компилятор, кажется, уже «делает все возможные выводы из пропущенных типов» и делает хорошую работу. Я имею в виду, я не сталкивался с ситуацией, когда я ожидал, что он выведет тип, и он не мог, кроме этого конструктора. Кажется, что-то пропущено, и это легко исправить, но, возможно, дело не в этом. Я не жалуюсь, просто пытаюсь уточнить. Triynko
@Dykam. Ты прав; согласованность с другими функциями желательна, но это далеко не единственное, что желательно при проектировании. Последовательность приятно иметь; Мы стараемся избегать ненужных несоответствий. Но согласованность с другими функциями очень низка в списке по сравнению с другими критериями дизайна, такими как стоимость. Eric Lippert
@Triynko: Я бы тоже этого хотел. Однако цель «компилятор делает все возможные выводы для пропущенных типов» явно не является целью языка C #. Это огромные затраты для достижения этой цели; эти ресурсы лучше потратить, добавив пользовательскую ценность другими способами. Возможно, вы бы чувствовали себя лучше, если бы вы программировали на F #? Он имеет значительно более продвинутые функции вывода типов, чем C #. Eric Lippert
1

Конструктор должен иметь ту же общую спецификацию, что и сам класс. В противном случае было бы невозможно узнать, еслиint в вашем примере будет относиться к классу или конструктору.

var obj = new MyType<int>(42);

Будет ли это классMyType<T> с конструкторомMyType(int) или классMyType с конструкторомMyType<T>(T)?

Эта двусмысленность может появиться и с обычными обобщенными методами. Dykam
Не было быневозможно знать в большинстве случаев. В тех случаях, когда это не может быть однозначно определено, что предназначалось, тогда мы могли бы дать ошибку. Это то же самое, что и любая другая проблема разрешения перегрузки; если предоставлена ​​недостаточная информация, мы просто выдаем ошибку. Eric Lippert
0

Хотя на это уже много раз отвечали, я чувствую, что мне нужно уточнить одну вещь: C #опоры вывод общего типа на конструкторы. Проблема в том, что он не поддерживает ни добавление общих параметров в конструкторы, нитип вывод общего типа. Желая вывести обобщенный аргумент типавведите сам в основном то же самое, что требуетFoo.Bar(0) сделать выводFoo<int>.Bar(0).

Похожие вопросы