2015-08-06 3 views
3

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

protocol DM1 { 
    func sayHi() -> Void 
} 

protocol DM2 { 
    func sayHello() -> Void 
} 

protocol VM1 { 
    typealias T: DM1 
    var dm: T { get } 
} 

protocol VM2 { 
    typealias T: DM2 
    var dm: T { get } 
} 

protocol RT: VM1, VM2 { 

} 

class James { 
    let rt: RT 

    init(rt: RT) { 
     self.rt = rt 
    } 
} 

Приведенных выше результаты кода ошибки в «„RT“протокола может быть использован только в качестве общего ограничения, поскольку он имеет Я, или связанные с ними требования типа» на rt переменный экземпляре и создание экземпляра параметра для James. Я действительно не понимаю, почему я не могу использовать это общее требование в своем классе James.

Я первоначально сделал что-то, как показано ниже:

protocol DM1 { 
    func sayHi() -> Void 
} 

protocol DM2 { 
    func sayHello() -> Void 
} 

protocol VM1 { 
    var dm: DM1 { get } 
} 

protocol VM2 { 
    var dm: DM2 { get } 
} 

protocol RT: VM1, VM2 { 

} 

struct Fred: DM1, DM2 { 
    func sayHi() { 
     println("hi") 
    } 
    func sayHello() { 
     println("hello") 
    } 
} 

struct Bob: RT { 
    let dm: Fred 
} 

class James { 
    let rt: RT 

    init(rt: RT) { 
     self.rt = rt 
    } 
} 

Но это не удается, потому что «Type„Bob“не соответствует протоколу„VM1“» (и VM2), который я могу сортировать понять, так как мой протокол требует, чтобы переменная имела конкретный тип протокола, а не какой-то тип экземпляра, который соответствует этому протоколу. Таким образом, вышеупомянутая версия должна была быть способом обойти это.

Кто-нибудь есть решение для того, что я хочу сделать (быть в состоянии сделать бетонную-структуру, которая соответствует RT, имея в качестве dm собственности конкретной структуры, которая соответствует как к DM1 и DM2)?

ответ

7

protocol RT наследует от protocol VM1 и protocol VM2, которые оба имеют typealias требований.

Протокол с требованием typealias может использоваться только как тип сдержанности, а не как тип.

Даже простой протокол, как это ...

protocol MyProtocol { 
    typealias Empty 
} 

... (что совершенно бесполезно) может быть использован только в качестве типа ограничения.

Существует некоторая информация на this link, которая может быть полезна.

EDIT:

Я сделаю все возможное, чтобы объяснить, почему протоколы с typealias требования могут быть использованы только в качестве ограничения типа.

Подумайте о протоколе как о контракте. Этот протокол обещает поставить изменяемое значение имени promise типа Int:

protocol PromiseIntType { 
    var promise: Int { get set } 
} 

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

class A { 
    var contract: PromiseIntType 
} 

... Вы знаете, что если вы пишете это ...

let intValue = A().contract.promise 

...что intValue будет Int. И если вы хотите, чтобы установить значение promise имущества типа-, вы знаете, что вам нужно будет поставлять Int для нового значения:

let a = A() 
a.contract.promise = 100 

Все условия договора известны заранее, и вы заранее знать, какие обещания и какие типы вы работаете.

PromiseIntType можно использовать точно так, как если бы он был определен в качестве фактического типа, например:

struct PromiseInt { 
    var promise: Int 
} 

Теперь бросить typealias требование в смеси:

protocol PromiseSomeType { 
    typealias Surprise 
    var promise: Surprise { get set } 
} 

Какое обещание делает PromiseSomeType сделать? В нем говорится, что он будет предоставлять изменяемое значение, называемое promise, но он не сообщает вам, какой тип это значение будет. Все это говорит вам, что все, что он предоставляет, будет Surprise. Не все условия контракта известны заранее. Некоторые детали будут заполнены позже.

Но это делает невозможным использование PromiseSomeType как типа. Посмотрите на этот класс, и спросите себя, что вы можете сделать с contract собственности:

class B { 
    var contract: PromiseSomeType 
} 

Например, как бы вы идти о создании его?

let b = B() 
b.contract.promise = <What type goes here?> 

Какое значение Типа вы получите, если вы попытались получить доступ к promise недвижимости?

let someValue = b.contract.promise // What type is someValue? 

[Дополнительные EDIT:

Как бы вы использовали someValue? Если это Int, то вы можете сделать это:

let newValue = someValue + 12 

Но у вас нет возможности узнать, во время компиляции, если someValue является Int или нет. Свифт настаивает на том, чтобы знать тип каждой константы, переменной и объекта во время компиляции, чтобы он мог проверить, являются ли действия, выполняемые вами по типу законными. Если это отсрочка этих определений во время выполнения, незаконные операции могут привести к сбою всей программы, и мы потеряем преимущества, которые обеспечивает безопасность типов.

/Дополнительная Редактировать]

Вы заполняете детали PromiseSomeType контракта при создании реального типа, который выполняет контракт:

struct PromiseInt: PromiseSomeType { 
    var promise: Int 
} 

PromiseInt говорит, что он будет выполнять PromiseSomeType контракт, и он заполняет недостающие сведения о том, какой тип будет promise.

Использование PromiseSomeType как тип ограничений может показаться, что он просто толкает неоднозначность вниз линии:

class C<T: PromiseSomeType> { 
    var contract: T 
} 

В этом случае детали договора заполняются в том, когда создается экземпляр универсального типа и указать фактический тип, который вы используете:

let c = C<PromiseInt>(contract: PromiseInt(promise: 0)) 
c.contract.promise = 100 // You know that promise is of type Int 

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

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

+0

Спасибо за ответ, и ссылка была, безусловно, полезна. Однако, хотя я понимаю, что я не могу использовать свой протокол как тип, потому что он имеет «typealias», я до сих пор не понимаю, почему это должно быть так - в чем смысл этого ограничения? И видите ли вы какой-либо способ сделать то, что я хотел бы сделать (предположительно, не используя 'typealias')? – Rupert

+0

См. Мой отредактированный ответ. Надеюсь, это поможет - различие является абстрактным, но все сводится к типу безопасности. –

+0

Благодарим вас за подробное объяснение. Я вижу, как может возникнуть проблема с изменяемой переменной, которая является протоколом с требованиями 'typealias', потому что конкретный класс, который имеет это свойство, будет ожидать, что на нем будет установлен только один тип, когда объявление предложит нам установить его в любой экземпляр, соответствующий этому протоколу. Но для неизменяемого свойства я бы подумал, что это не проблема, потому что мы можем вернуть только тот конкретный экземпляр. Я просто предполагаю, что разрешить его для неизменных свойств будет слишком сложно. – Rupert