2016-11-03 6 views
1

У меня есть протокол, который имеет функцию, которая может возвращать String или [String: String]. Это мое заявление:Как определить общий тип из реализации протокола

protocol Test { 
    associatedtype T: Hashable 
    func returnSomething() -> T 
} 

Тогда я хочу реализацию по умолчанию для returnSomething, поэтому я сделал расширение протокола:

extension Test { 
    func returnSomething() -> T { 
     let valueToReturn = readValueFromPLISTthatCanReturnAStringOrDictionary() as T 
     return valueToReturn 
    } 
} 

Итак, наконец, у меня есть 2 clases, TestString и TestDictionary, что оба орудия Test протокола и я хочу указать параметр T, и я хочу использовать реализацию по умолчанию. Как я это делаю?

class TestString: Test {} 

class TestDictionary: Test { } 

class TestString: Test where Test.T = String or similar? 
+0

Вашей проблемы довольно абстрактно, и я не вполне понимаю. Не могли бы вы рассказать? – Alexander

+0

Реализация по умолчанию всегда используется, если вы не переопределяете ее, поэтому я не уверен, что проблема – Alexander

+0

Я хочу использовать реализацию по умолчанию, но я не могу ее использовать, потому что TestString и TestDictionary не сообщают Протокол испытаний, общий тип которого является T. – Godfather

ответ

2

У меня есть протокол, который имеет функцию, которая может возвращать String или [String: String]. Это мое заявление:

Нет проблем. Давайте это напишем.

enum StringOrDictionary { 
    case string(String) 
    case dictionary([String: String]) 
} 

protocol Test { 
    func returnSomething() -> StringOrDictionary 
} 

Тогда я хочу реализацию по умолчанию для returnSomething, поэтому я сделал расширение протокола:

хорошо звучит.Я буду считать, что readValueFromPLISTthatCanReturnAStringOrDictionary() действительно возвращает Any, так как это то, что возвращается propertyList(from:).

extension Test { 
    func returnSomething() -> StringOrDictionary { 
     let value = readValueFromPLISTthatCanReturnAStringOrDictionary() 

     switch value { 
     case let string as String: return .string(string) 
     case let dictionary as [String: String]: return .dictionary(dictionary) 
     default: fatalError() // Or perhaps you'd like to do something else 
     } 
    } 
} 

Это было бы замечательно, чтобы назвать свой тип что-то более значимое, чем StringOrDictionary, но, кроме того, оно должно быть довольно просто. Просто введите тип, который означает то, что вы говорите. Вам нужен тип, который означает «OR», и это перечисление. (Если вы хотите, типа, который означает «И» это структура BTW.)


Что касается вашего ответа, это не законно:

class RandomClass: Test where Test.T == String { 
    func getValue() { 
     let bah = doSomething() // I don't need here to specify bah's type. 
    } 
} 

Пути определить ваш T является реализация требуемый метод.

class RandomClass: Test { 
    func returnSomething() -> String { 
     return "" 
    } 
} 

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

Обновление: По-видимому, они добавили новую функцию, о которой они говорили, но я думал, что еще нет. Вы могли теперь реализовать RandomClass таким образом:

class RandomClass: Test { 
    typealias T = String 
} 

(Который очень хорошая новая функция, даже если это не хороший ответ на эту проблему.)

+0

, который выглядит красиво! Кажется ясным для меня. В любом случае, можете ли вы взглянуть на мой ответ? – Godfather

+0

Красивые :) Проголосуйте. – Alexander

+1

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

0

Вы должны указать typealias при расширении класса, например, так:

protocol Test { 
    associatedtype T: Hashable 
    func returnSomething() -> T 
} 

extension String: Test { 
    typealias T = Int 
} 

func def() -> Int { 
    return 6 
} 

extension Test { 
    func returnSomething() -> T { 
     return def() as! Self.T 
    } 
} 

"".returnSomething() 

Однако, я не мог найти способ сделать это без принудительного литья.

+0

Это не то, о чем я прошу. В основном я хочу, чтобы класс реализовал этот протокол и имел возможность использовать функцию returnSomethingFunction. Но для этого мне нужно указать класс, который будет использоваться. Если я переместил общий параметр в функцию, все работа спросит, ожидайте, так что func testSomething () -> T, а затем функция знает, что вернуть, когда я делаю aDict: [String: String] = ATestDictionary.returnSomething() – Godfather

1

Вот решение вашей насущной проблемой:

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

Следующая проблема здесь заключается в том, что [String: String] не является Hashable. Это связано с отсутствием поддержки условных соответствий (например, возможность выразить, что Dictionary - это Hashable, если ключи и значения оба Hashable), один из крупнейших спадов Swift, IMO. Вероятно, вы захотите использовать обертку стирания типа AnyHashable.

protocol ResultProvider { 
    associatedtype Result: Hashable 
    func getResult() -> Result 
} 

protocol StringResultProvider: ResultProvider { 
    typealias Result = String 
} 

extension StringResultProvider { 
    func getResult() -> String { 
     return "A string result" 
    } 
} 

protocol IntResultProvider: ResultProvider { 
    typealias Result = Int 
} 

extension IntResultProvider { 
    func getResult() -> Int { 
     return 123 
    } 
} 

class TestIntResult: IntResultProvider {} 
class TestString: StringResultProvider {} 

print(TestString().getResult()) 
print(TestIntResult().getResult()) 


// protocol DictionaryResultProvider: ResultProvider { 
//  typealias Result = [String: String] 
// } 

// extension DictionaryResultProvider { 
//  func getResult() -> [String: String] { 
//   return ["A dictionary": "result"] 
//  } 
// } 

// class TestDictionaryProvider: DictionaryResultProvider {} 
+0

thx вы! посмотрите и мой ответ на альтернативное решение. – Godfather

+0

Обходит ли проблема с использованием 'Any' и принудительных отчислений в качестве решения? – Alexander

+0

Это был плохой пример, возвращающий любые, я обновил свой ответ, чтобы уточнить. – Godfather

0

Единственное рабочее решение, которое является общим в функции, и указывает тип переменной при вызове функции. Мне было интересно, если я мог бы указать тип T, когда я реализацию протокола в классе, похожий, как это:

class RandomClass: Test where Test.T == String { 
    func getValue() { 
     let bah = doSomething() // I don't need here to specify bah's type. 
    } 
} 

Но предыдущий пример просто не работает, поэтому альтернатива может быть такой:

protocol Test { 
    func doSomething<T>() -> T 
} 

extension Test { 
    func doSomething<T>(key: String) -> T { 
     return returnDictOrStringFromPLIST(key: key) as! T 
    } 
} 

class TestString: Test { 
    func getValue() { 
     let bah: String = doSomething() 

    } 
} 

class TestDict: Test { 
    func getValue() { 
     let bah: [String: String] = doSomething() 

    } 
} 
Смежные вопросы