Подтвердить что ты не робот

Общая функция, использующая имя типа в Swift

В С# можно вызвать общий метод, указав тип:

public T f<T>()
{
   return something as T
}

var x = f<string>()

Swift не позволяет вам специализировать общий метод при его вызове. Компилятор хочет полагаться на вывод типа, поэтому это невозможно:

func f<T>() -> T? {
    return something as T?
}

let x = f<String>() // not allowed in Swift

Мне нужен способ передать тип функции, и эта функция возвращает объект этого типа, используя generics

Это работает, но это не очень подходит для того, что я хочу сделать:

let x = f() as String?

РЕДАКТИРОВАТЬ (ПОДТВЕРЖДЕНИЕ)

Я, вероятно, не совсем понял, что вопрос на самом деле, все о более простом синтаксисе для вызова функции, которая возвращает данный тип (любой тип).

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

// returns the first element in the array of that type
func findFirst<T>(array: [Any]) -> T? {
    return array.filter() { $0 is T }.first as? T
}

Вы можете вызвать эту функцию следующим образом:

let array = [something,something,something,...]

let x = findFirst(array) as String?

Это довольно просто, но что, если возвращаемый тип - это какой-то протокол с методом и вы хотите вызвать метод на возвращаемом объекте:

(findFirst(array) as MyProtocol?)?.SomeMethodInMyProtocol()
(findFirst(array) as OtherProtocol?)?.SomeMethodInOtherProtocol()

Этот синтаксис просто неудобен. В С# (который так же сильно напечатан как Swift) вы можете сделать это:

findFirst<MyProtocol>(array).SomeMethodInMyProtocol();

К сожалению, это невозможно в Swift.

Итак, вопрос: есть способ сделать это с помощью более чистого (менее неудобного) синтаксиса.

4b9b3361

Ответ 1

К сожалению, вы не можете явно определить тип универсальной функции (используя синтаксис <...> для нее). Тем не менее, вы можете предоставить универсальный T.Type (T.Type) в качестве аргумента функции, чтобы позволить Swift выводить универсальный тип функции, как сказал Роман.

Для вашего конкретного примера вы хотите, чтобы ваша функция выглядела примерно так:

func findFirst<T>(in array: [Any], ofType _: T.Type) -> T? {
  return array.lazy.compactMap { $0 as? T }.first
}

Здесь мы используем compactMap(_:), чтобы получить последовательность элементов, которые были успешно преобразованы в T, а затем first получить первый элемент этой последовательности. Мы также используем lazy чтобы мы могли перестать оценивать элементы после нахождения первого.

Пример использования:

protocol SomeProtocol {
    func doSomething()
}

protocol AnotherProtocol {
    func somethingElse()
}

extension String : SomeProtocol {
    func doSomething() {
        print("success:", self)
    }
}

let a: [Any] = [5, "str", 6.7]

// outputs "success: str", as the second element is castable to SomeProtocol.
findFirst(in: a, ofType: SomeProtocol.self)?.doSomething()

// doesn't output anything, as none of the elements conform to AnotherProtocol.
findFirst(in: a, ofType: AnotherProtocol.self)?.somethingElse()

Обратите внимание, что вы должны использовать .self для ссылки на метатип определенного типа (в данном случае SomeProtocol). Возможно, не такой уж интересный синтаксис, на который вы нацеливались, но я думаю, что он настолько хорош, насколько это возможно.

Хотя в этом случае стоит отметить, что эту функцию лучше разместить в расширении Sequence:

extension Sequence {
  func first<T>(ofType _: T.Type) -> T? {
    // Unfortunately we can't easily use lazy.compactMap { $0 as? T }.first
    // here, as LazyMapSequence doesn't have a 'first' property (we'd have to
    // get the iterator and call next(), but at that point we might as well
    // do a for loop)
    for element in self {
      if let element = element as? T {
        return element
      }
    }
    return nil
  }
}

let a: [Any] = [5, "str", 6.7]
print(a.first(ofType: String.self) as Any) // Optional("str")

Ответ 2

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

protocol SomeProtocol {
    init()
    func someProtocolMethod()
}

Затем добавьте T.Type в качестве параметра в свой метод:

func f<T: SomeProtocol>(t: T.Type) -> T {
    return T()
}

Тогда предположим, что у вас есть тип, соответствующий SomeProtocol следующим образом:

struct MyType: SomeProtocol {
    init() { }
    func someProtocolMethod() { }
}

Затем вы можете вызвать свою функцию следующим образом:

f(MyType.self).someProtocolMethod()

Как отмечали другие, это кажется запутанным способом делать то, что вы хотите. Если вы знаете тип, например, вы можете просто написать:

MyType().someProtocolMethod()

Нет необходимости в f.