2016-03-23 7 views
1
protocol ProtocolA { 
    func someFunc() -> Any 
} 

protocol ProtocolB: ProtocolA { 
    var someVar: Any { get } 
} 

enum MyEnum<T: ProtocolA, U: ProtocolB> { 
    case A(T) 
    case B(U) 
} 

protocol DataObject { 
    ... 
} 

extension DataObject where Self: ProtocolB { 
    func doSomething() { 
     let x = MyEnum.B(self) 
     /// Compiler error: 
     /// Cannot invoke 'B' with an argument list of type '(Self)' 
    } 
} 

Yukarıdakilerin neden bana bir hata verdiğini anlamıyorum. Tuhaf bir şekilde, iki enum genel kısıtlamalarından birini kaldırarak, enum'u tek bir kısıtlama ile kaldırarak sorunu çözer ...Swift enum çeşitli protokolleri içeren birden çok genel tür içerir

ProtocolB'nin ProtocolA'yı genişleteceğini unutmayın. Bu genel kısıtlamalar ile kullanıldığında desteklenmiyor mu?

cevap

2

hata olduğunu ... Hala oldukça neden alamadım Ancak

enum MyEnum { 
    typealias T = ProtocolA 
    typealias U = ProtocolB 

    case A(T) 
    case B(U) 
} 

: Buna MyEnum değiştirilmesi GÜNCELLEME


sorunu çözer biraz yanıltıcı. Sorunu keşfetmek için sorunu basitleştirelim.

protocol ProtocolA {} protocol ProtocolB: ProtocolA {} enum MyEnum<T: ProtocolA, U: ProtocolB> { case A(T) case B(U) } struct AThing: ProtocolA {} struct BThing: ProtocolB {} let bThing = BThing() let thing = MyEnum.B(bThing) // error: cannot invoke 'B' with an argument list of type '(BThing)' 

Şimdi biz DataObject gerek kalmadan aynı sorun var. Bu neden başarısız?

MyEnum, genel amaçlıdır. Bu, bir beton türü oluşturmak için, T ve U türlerini bilmesi gerektiği anlamına gelir. Bu türler üzerinde kısıtlamalar verdiniz (bunlardan biri ProtocolA ve diğeri ProtocolB ile uyumludur), ancak hangi tür olduklarını özellikle söylemediniz.

biz bu hat almak:

let thing = MyEnum.B(bThing) 

thing ne tür

? Yazım çıktısı ile, U'un ne olduğunu öğrenebiliriz, ancak T nedir?

let thing: MyEnum<?, BThing> = MyEnum.B(bThing) 

Orada sadece yeterli bağlam onu ​​yürütmek için burada, bu yüzden açıkça derleyici anlatmak zorunda:

let thing = MyEnum<AThing, BThing>.B(bThing) 

Yani thing tam tür MyEnum<OtherAThing, BThing> farklı bir türüdür MyEnum<AThing, BThing> olduğu . işleri

Kişisel İkinci örnek jenerik değil (Aynen gibi [Int]. let xs = [] açık bir tip tanımı olmadan derlemek olmaz yüzden [String] farklı türüdür), bu yüzden herhangi bir problem yoktur.

enum MyEnum { 
    case A(ProtocolA) 
    case B(ProtocolB) 
} 

Bound typealiases sadece türlerini adlandırmak, onlar (2.2 çağrılar bunu, associatedtype olur, ya da Swift bir ilişkisiz typealias gibi) yeni türleri oluşturmak değildir: O kadar kolaylaştırır. Yani biz AProtocolA ve B herhangi bir ProtocolB alır ve tüm MyEnum örnekleri aynı türde olduğunu biliyoruz.

Derleyici karıştırıldığı için hata talihsiz bir durumdur. Bunu en basit durum için basitleştirdiyseniz daha net bir hata alırsınız. Bu, örneğinizle aynı nedenden dolayı başarısız olur.

enum MyEnum<T> { 
    case A 
} 

let x = MyEnum.A // error: generic parameter 'T' could not be inferred 
+0

Açıklama için teşekkürler! Bu altın bir cevap daha fazla burada görmek istiyorum;) Gerçekten de hata çok talihsiz burada :) Şimdilik 'typealias 'çözüm ile sopa. Hala ilgili protokole uyan herhangi bir şeyi geçmeme izin veriyor. (Yani jenerik ile çözüm gibi). Sanırım bu, girdiyi böyle bir şey yapmak yerine, sadece tek bir protokolle sınırlandırmamı sınırlandırıyor: “T burada T: ProtocolA, T: ProtocolC>? – Cabus

+0

Bu kısıtlamadan ne beklediğinizi bilmiyorum. "Or?" Demek istiyor musunuz? Durum buysa, T türü nedir? Hangi yöntemleri çağırırsın? Bir türü olmalı. Üst üste binen bazı protokoller grubu varsa, bunların hepsinin uydukları bir protokol daha kurabilirsiniz. –

+0

Gecikme için özür dilerim. O zamandan beri başka bir çözüme geçtim. Yine de yardımın için teşekkürler. Ancak: Lütfen bu http://www.youtube.com/watch?v=gaXSbqo3iKA&t=2m54s 'a bir bakın. Çalışması gerektiğini söylüyor? – Cabus