Swift : 제네릭 유형이 프로토콜을 준수하는지 확인


80

다음과 같이 정의한 프로토콜이 있습니다.

protocol MyProtocol {
   ...
}

또한 일반 구조체가 있습니다.

struct MyStruct <T>  {
    ...
}

마지막으로 일반적인 기능이 있습니다.

func myFunc <T> (s: MyStruct<T>) -> T? {
   ...
}

T 유형이 MyProtocol을 준수하는지 함수 내부를 테스트하고 싶습니다. 본질적으로 나는 할 수 있기를 원합니다 (~ 의사 코드) :

let conforms = T.self is MyProtocol

그러나 이것은 컴파일러 오류를 발생시킵니다.

error: cannot downcast from 'T.Type' to non-@objc protocol type 'MyProtocol'
   let conforms = T.self is MyProtocol
                  ~~~~~~ ^  ~~~~~~~~~~

나는 또한 변화 같은 시도 T.self is MyProtocol.self, T is MyProtocol그리고 사용하는 ==대신을 is. 지금까지 나는 아무데도 가지 않았다. 어떤 아이디어?

답변:


80

@Alex가 T유형이 아닌 프로토콜을 준수 하는지 확인하고 싶습니다 s. 그리고 일부 응답자는 명확하게 보지 못했습니다.

확인 T유형은 다음과 같은 프로토콜을 따릅니다.

if let _ = T.self as? MyProtocol.Type {
    //  T conform MyProtocol
}

또는

if T.self is MyProtocol.Type {
    //  T conform MyProtocol
}

10
MyProtocol이 CaseIterable (관련 유형이있는 프로토콜)이면 어떻게됩니까? 오류를 표시합니다. 'CaseIterable'은 자체 또는 연관된 유형 요구 사항이 있기 때문에 일반 제약 조건으로 만 사용할 수 있습니다.
zgjie

75

조금 늦었지만 테스트로 프로토콜에 응답하는지 테스트 할 수 있습니다 as ?.

if let currentVC = myViewController as? MyCustomProtocol {
    // currentVC responds to the MyCustomProtocol protocol =]
}

편집 : 조금 더 짧게 :

if let _ = self as? MyProtocol {
    // match
}

그리고 가드 사용 :

guard let _ = self as? MyProtocol else {
    // doesn't match
    return
}

4
의 인스턴스가 필요 T하지만 질문은 일반 TYPE에 대해 묻습니다. 따라서 Carlos 대답이 더 좋습니다. stackoverflow.com/a/52787263/1311272
Sajjon dec

Carlos Chaguendo의 답변은 IMO에서 허용되는 답변이어야합니다.
J.beenie

이것은 질문에 대한 답이 아닙니다
Jonathan.

36

가장 간단한 대답은 : 그렇게하지 마십시오. 대신 오버로딩 및 제약 조건을 사용하고 런타임에 동적으로 테스트하는 대신 컴파일 타임에 모든 것을 미리 결정하십시오. 런타임 유형 검사 및 컴파일 타임 제네릭은 스테이크와 아이스크림과 비슷합니다. 둘 다 좋지만 혼합하는 것은 약간 이상합니다.

다음과 같은 것을 고려하십시오.

protocol MyProtocol { }

struct MyStruct <T>  { let val: T }

func myFunc<T: MyProtocol>(s: MyStruct<T>) -> T? {
    return s.val
}

func myFunc<T>(s: MyStruct<T>) -> T? {
    return nil
}

struct S1: MyProtocol { }
struct S2 { }

let m1 = MyStruct(val: S1())
let m2 = MyStruct(val: S2())

myFunc(m1) // returns an instance of S1
myFunc(m2) // returns nil, because S2 doesn't implement MyProtocol

단점은 T가 런타임에 프로토콜을 지원하면 동적으로 설정할 수 없다는 것입니다.

let o: Any = S1()
let m3 = MyStruct(val: o)
myFunc(m3)  // will return nil even though o 
            // does actually implement MyProtocol

그러나 솔직히 말해서 일반 함수 내에서 그렇게해야합니까? 실제 유형이 무엇인지 확실하지 않은 경우 더 나은 옵션은 나중에 지연하고 알아 내기 위해 제네릭 함수 내에서 찌르는 것보다 미리 파악하는 것입니다.


17
+1, 좋은 답변입니다. 특히 즐거웠습니다. " 런타임 유형 검사 및 컴파일 타임 제네릭은 스테이크와 아이스크림과 비슷합니다. 둘 다 좋지만 혼합하는 것은 약간 이상합니다. "👍
Stuart

6
예… 과부하와 제약으로 인해 제대로 작동하지 않는 경우는 예외입니다. JSONEncodable을 요구 하는 프로토콜을 구현하는 확장 메서드를 고려하십시오 init(json: JSON) throws. 우리는 Array구현 JSONEncodable하고 싶지만, 그 요소도 마찬가지인 경우에만 가능합니다 JSONEncodable. 우리는 우리가 우리의 구현 내부 유형 검사의 어떤 종류를 사용해야합니다, 제약 상속 절을 결합 할 수 없습니다 init, 그리고이 경우 아마도 오류가 발생 Element하지 않습니다 JSONEncodable. 슬프게도 이것은 AFAICT가 불가능한 것 같습니다.
Gregory Higley

위의 수수께끼는 중간 유형을 썽 크로 사용하여 해결할 수 있다고 덧붙여 야하는데, 그것은 매우 비정상적인 해결책입니다.
Gregory Higley

4.1 (스위프트의 조건을 준수하여 지금 가능해야 @GregoryHigley swift.org/blog/conditional-conformance )
mj_jimenez

MyStruct힌트를 사용하거나 사용하지 않고 구성하는 방법이 멋지고 <Type>무엇을 해야할지 알려줄 수 있습니다. , 스위프트 4 필요로하는 코드 아웃하려고 다른 사람에 대한 _첫 번째 생성자에 인수
snakeoil

13

여러 유형의 케이스를 처리하려는 경우 swift의 스위치 케이스 패턴 일치를 활용할 수도 있습니다 T.

func myFunc<T>(s: MyStruct<T>) -> T? {
    switch s {
    case let sType as MyProtocol:
        // do MyProtocol specific stuff here, using sType
    default:
        //this does not conform to MyProtocol
    ...
    }
}

이것은 또한 인스턴스에서 테스트하는 것이 유형이 아닌 프로토콜을 직접 따르기 때문에 질문에 대답하지 않습니다.
조나단.


5

프로토콜을 @objc다음 과 같이 선언해야합니다 .

@objc protocol MyProtocol {
    ...
} 

Apple의 "The Swift Programming Language"책에서 발췌 :

위의 HasArea 프로토콜에서 볼 수 있듯이 프로토콜이 @objc 속성으로 표시된 경우에만 프로토콜 적합성을 확인할 수 있습니다. 이 속성은 프로토콜이 Objective-C 코드에 노출되어야 함을 나타내며 Cocoa 및 Objective-C와 함께 Swift 사용에 설명되어 있습니다. Objective-C와 상호 운용되지 않더라도 프로토콜 적합성을 확인하려면 @objc 속성으로 프로토콜을 표시해야합니다.

또한 @objc 프로토콜은 구조 나 열거가 아닌 클래스에 의해서만 채택 될 수 있습니다. 적합성을 확인하기 위해 프로토콜을 @objc로 표시하면 해당 프로토콜을 클래스 유형에만 적용 할 수 있습니다.


이것에도 불구하고 여전히 같은 오류가 발생합니다. @objc protocol MyProtocol {} struct MyStruct <T> {} func myFunc <T> (s: MyStruct<T>) -> T? { let conforms = T.self is MyProtocol }
Alex

1
@Alex, 프로토콜 적합성을 확인하기 전에 T 유형의 인스턴스를 생성해야합니다 (내가 아는 바와 같이) T 유형이 MyProtocol을 준수하는 것보다 유형 만 필요하면 다음과 같이 지정할 수 있습니다.func myFunc<T: MyProtocol>(...) -> T?
rabbitinspace

0

테스트 케이스의 경우 다음과 같이 적합성을 확인합니다.

let conforms: Bool = (Controller.self as Any) is Protocol.Type

0

현대적인 대답은 다음과 같습니다. (Swift 5.1)

func myFunc < T: MyProtocol> (s: MyStruct<T>) -> T? {    ... }

4
?? 질문에 대한 답은 무엇입니까?
Peter Schorn
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.