3

Например:Как проверить/предикат типа функции в Swift?

 
func f(x: Int) -> Int { 
    return x 
} 


func h(f: @escaping (Int) -> Any) { 
    if (f is (Int) -> Int) { 
     print(f(1)) 
    } else { 
     print("invalid") 
    } 
} 


h(f: f) 

Я ожидаю, что это распечатать 1, но это на самом деле печатает invalid.

+0

A kludgey way: 'if f (1) is Int {' – vacawama

+0

Я подозреваю, что тип экземпляра, «завернутый» с помощью 'Any' в поставляемом закрытии' f', не может быть выведен компилятором без фактического вызова 'f '. В глазу компилятора 'f' является замыканием на форме' {arg in;/* ... * /; return someInstanceOfTypeAny} '. Независимо от того, является ли 'someInstanceOfTypeAny' фактически типом' Int' или нет, может быть установлено только путем фактического изучения типа фактического экземпляра _, возвращаемого закрытием 'f'_, например. как предлагает @vacawama. Ниже приведен пример использования шаблона, где 'T' фактически является _concrete type_ (а не оболочкой времени выполнения!) Для каждого вызова' h'. – dfri

+0

@dfri При передаче 'f' в' h' компилятор знает, что тип 'f' is' (Int) -> Int', интересно, почему он не может сузить '(Int) -> Any' до подтипа' (Int) -> Int'? 'f' не является анонимным закрытием без аннотации типа. 'f' хорошо аннотируется. – weakish

ответ

3

Там обходной путь с помощью дженериков:

func intF(x: Int) -> Int { 
    return x 
} 

func stringF(x: Int) -> String { 
    return "\(x)" 
} 

func h<T>(f: (Int) -> T) { 
    if (T.self == Int.self) { 
     print(f(1)) 
    } else { 
     print("invalid") 
    } 
} 

h(f: intF) // prints: 1 
h(f: stringF) // prints: invalid 
1

Использование Any почти всегда признак кода запаха, вы должны попытаться полагаться как можно больше о безопасности типа, что Swift обеспечивает. Вы можете добиться этого, сделав h общий, таким образом проверяемый во время компиляции.

// the overload that does the actual stuff 
func h(f: @escaping (Int) -> Int) { 
    print(f(1)) 
} 

// this maps to all other types 
func h<T>(f: @escaping (Int) -> T) { 
    print("invalid") 
} 

h { _ in return "15" } // Invalid 
h { 2 * $0 }   // 2 

Heck, вы можете даже отказаться от родовой перегрузки, таким образом, вы будете иметь для свободных проверок компиляции вместо сбоев во время выполнения (много, много надежных и прогнозирующих)

+0

Я написал 'Any' so' (Int) -> Int' - это подтип '(Int) -> Any'. Это сделать пример кода коротким, не определяя много классов. – weakish

0

Вы можете переписать h в общий функция:

func h<T>(f: @escaping (Int) -> T) {   
    if T.self == Int.self { 
     print(f(1)) 
    } else { 
     print("invalid") 
    } 
} 

Но лучший способ писать перегруженные типа специфичные для h и общий улов-все для отдыха (если вам это нужно вообще).

+0

Это то же самое, что @nils отвечает? – weakish