Могу ли я использовать заданный тип заполнителя на основе возвращаемого значения? - PullRequest
0 голосов
/ 09 мая 2018

Я хотел бы указать в протоколе функцию, возвращаемое значение которой соответствует типу текущего класса реализации.

protocol MakesSelf {
    static func getInstance<T>() -> T
}

За исключением случаев, когда T ограничен тем же типом того, кто реализует MakesSelf, например

class MyThing: MakesSelf {

    class func getInstance<T>() -> T {
        return MyThing()
    }

}

Это было мое понимание того, что компилятор назначит T тип MyThing на основе возвращаемого значения, но вместо этого я получаю ошибку преобразования типа: Cannot convert return expression of type 'MyThing' to return type 'T'

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

Проблема, с которой я столкнулся, заключалась в том, что T не может быть выведено в случае MyThing.Компилятор не очень полезен, когда дело доходит до сообщений об ошибках (однажды мне сказали Cannot convert return expression of type 'T' (aka 'MyImplementation') to return type 'T')

Решение состоит в том, чтобы дать компилятору какой-то способ узнать, каким должен быть T (еслиВы хотели, чтобы это было без ограничений, используйте AnyObject, а не T).

В моем случае я хотел вернуть значение типа Self (тип реализации).Это усложняет вещи больше, чем ваши обычные проблемы с POP, поскольку вы не можете ссылаться на Self в классе / статической функции.

Чтобы обойти это, я использую Swift associatedtype, который позволяет нам устанавливать именованный заполнительдля использования по протоколу.Поскольку мы определяем это на уровне протокола и предоставляем значение, мы можем установить именованную ссылку на собственный тип разработчиков.

associatedtype MyType = Self.Type 

Примечание , что associatedtype MyType = Self не будетработать, поскольку self в этом контексте является протоколом , а не типом окончательной реализации (о котором мы пока не знаем, и мы / компилятор не узнаем, пока какой-то объект не реализует протокол).

Поскольку мы предоставляем значение, когда мы реализуем наш протокол, тип уже ограничен!Поскольку ограничение относится только к типу реализации, реализации протокола достаточно для определения типа (круто).Самое приятное то, что теперь я могу исключить ссылку на Self в моем class func определении:

protocol MakesSelf {

    associatedtype MyType = Self.Type

    static func getInstance() -> MyType

}

class MyImplementation: MakesSelf {

    class func getInstance() -> MyImplementation {
        print("Hello")
        return MyImplementation()
    }

}

let myThing = MyImplementation.getInstance() 

Чтобы быть супер ясным здесь - класс MyImplementation имеет typealias, называемый MyType, как указано в протоколе.Когда я компилирую, псевдоним будет ссылаться на текущую реализацию.Теперь, когда я реализую функцию getInstance, MyImplementation соответствует MyType.

Обратите внимание , что я должен написать свою подпись функции в виде class func getInstance() -> MyImplementation, а не class func getInstance() -> MyType.Это потому, что я возвращаю результат вызова функции MyImplementation(), который, как оказалось, соответствует ограничению MyType.Это не то же самое, что сказать, что MyImplementation может быть неявно преобразовано в MyType.

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

0 голосов
/ 09 мая 2018

Вы можете создать протокол с соответствующим типом, в котором ваша функция getInstance() будет возвращать этот тип. В расширении протокола создайте реализацию этой функции по умолчанию. Полный пример:

protocol Initializable {
    init()
}

protocol MakesSelf: Initializable {
    associatedtype Object: Initializable = Self
    static func getInstance() -> Object
}

extension MakesSelf {
    static func getInstance() -> Object {
        return Object()
    }
}

class MyThing: MakesSelf {
    required init() {}

    func printSelf() {
        print(MyThing.self)
        print(Object.self)
        // Both will print "MyThing" because Object is a typealias of MyThing class
    }
}

Тогда получите ваш экземпляр:

let instance = MyThing.getInstance()
print("\(instance.self)")
/* Will print: MyTestApp.MyThing */

Как вы можете видеть, потому что вы уже дали реализацию по умолчанию getInstance() в расширении протокола, которое не является необходимым в соответствующем классе. В контексте вашего вопроса связанный тип служит «заполнителем».

...