Проблема с дженериками

#generics #f#

#дженерики #f#

Вопрос:

Почему следующий код не компилируется? Это ошибка компилятора или особенность языка? Каков наилучший обходной путь?

 type A() as this =
    let a = this.GetTypedObject<int>()    // a
    let b = this.GetTypedObject<string>() // b

    member this.GetTypedObject<'T>() =
        Unchecked.defaultof<'T>
  

Не проверено.defaultof<‘T> используется только для примера, вместо этого можно использовать вызов любой функции или конструктора.

Компилятор сообщает, что код становится менее универсальным в строке (a) и отказывается компилировать строку (b). Я не могу дать точные сообщения компилятора, потому что у меня они на русском :).

Превращение метода GetTypedObject() в привязку let и удаление <‘T> приводит к другому предупреждению компилятора, которое мне тоже не нравится. Единственный способ, который я нашел, это переместить GetTypedObject<‘T>() в базовый класс и сделать его общедоступным. Заранее спасибо…

Ответ №1:

Обратите внимание, что вы можете просто добавить аннотацию типа, чтобы исправить это:

 type A() as this = 
    let a = this.GetTypedObject<int>()    // a 
    let b = this.GetTypedObject<string>() // b 

    member this.GetTypedObject<'T>() : 'T = 
                                   //^^^^
        Unchecked.defaultof<'T> 
  

Сначала считываются подписи элементов, затем все тела let и member, когда дело доходит до порядка вывода типа. Ввод возвращаемого типа в подпись объявления делает его видимым для lets.

Комментарии:

1. Спасибо. Не мог представить, что решение может быть таким простым 🙂

Ответ №2:

Поскольку вывод типа выполняется сверху вниз, он обнаруживает GetTypedObject возврат int еще до того, как достигнет определения метода, и обнаруживает, что он должен быть универсальным. (Как указывает Брайан, элементы считываются перед let привязками…и для этого есть более простое решение.)Я получаю следующую неприятную ошибку:

Использование функции ‘GetTypedObject’ не соответствует типу, выведенному в другом месте. Предполагаемый тип функции — Test.A -> Microsoft.FSharp.Core.unit -> ‘a. Тип функции, необходимой на данном этапе использования, — Test.A -> Microsoft.FSharp.Core.unit -> ‘a Эта ошибка может быть вызвана ограничениями, связанными с универсальной рекурсией в коллекции ‘let rec’ или в группе классов. Рассмотрите возможность предоставления полной подписи типа для целевых объектов рекурсивных вызовов, включая аннотации типов как для аргументов, так и для возвращаемых типов.

Если использование появляется после определения метода, оно работает.

 type A() =
    member this.GetTypedObject<'T>() =
        Unchecked.defaultof<'T>

    member this.Test() =
      let a = this.GetTypedObject<int>()    // a
      let b = this.GetTypedObject<string>() // b
      ()
  

Вот обходной путь:

 type A() =
  let getTypedObject() = Unchecked.defaultof<_>
  let a : int = getTypedObject()    // a
  let b : string = getTypedObject() // b

  member this.GetTypedObject<'T>() : 'T = getTypedObject()
  

Комментарии:

1. Большое спасибо! Для меня сработало как по волшебству.