在 Swift 中采用类型名称的泛型函数 [英] Generic function taking a type name in Swift

查看:27
本文介绍了在 Swift 中采用类型名称的泛型函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 C# 中,可以通过指定类型来调用泛型方法:

In C#, it's possible to call a generic method by specifying the type:

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

var x = f<string>()

Swift 不允许您在调用泛型方法时对其进行专门化.编译器要依赖类型推断,所以这是不可能的:

Swift doesn't allow you to specialize a generic method when calling it. The compiler wants to rely on type inference, so this is not possible:

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

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

我需要的是一种将类型传递给函数的方法,该函数使用泛型返回该类型的对象

What I need is a way to pass a type to a function and that function returning an object of that type, using generics

这行得通,但不适合我想做的事情:

This works, but it's not a good fit for what I want to do:

let x = f() as String?

编辑(澄清)

我可能不太清楚问题究竟是什么,这都是关于调用返回给定类型(任何类型)的函数的更简单的语法.

I've probably not been very clear about what the question actually is, it's all about a simpler syntax for calling a function that returns a given type (any type).

举一个简单的例子,假设您有一个 Any 数组,并且您创建了一个返回给定类型的第一个元素的函数:

As a simple example, let's say you have an array of Any and you create a function that returns the first element of a given type:

// 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
}

你可以这样调用这个函数:

You can call this function like this:

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

let x = findFirst(array) as String?

这很简单,但是如果返回的类型是带有方法的某种协议并且您想在返回的对象上调用该方法怎么办:

That's pretty simple, but what if the type returned is some protocol with a method and you want to call the method on the returned object:

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

这种语法很笨拙.在 C#(与 Swift 一样强类型)中,您可以这样做:

That syntax is just awkward. In C# (which is just as strongly typed as Swift), you can do this:

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

遗憾的是,这在 Swift 中是不可能的.

Sadly, that's not possible in Swift.

所以问题是:有没有办法用更简洁(不那么笨拙)的语法来实现这一点.

推荐答案

遗憾的是,您不能显式定义泛型函数的类型(通过在其上使用 <...> 语法).但是,您可以提供一个泛型元类型(T.Type)作为函数的参数,以允许 Swift 推断泛型类型正如罗马所说.

Unfortunately, you cannot explicitly define the type of a generic function (by using the <...> syntax on it). However, you can provide a generic metatype (T.Type) as an argument to the function in order to allow Swift to infer the generic type of the function, as Roman has said.

对于您的具体示例,您希望您的函数看起来像这样:

For your specific example, you'll want your function to look something like this:

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

这里我们使用 compactMap(_:) 来获取成功转换为 T 的元素序列,然后是 firstcode> 获取该序列的第一个元素.我们还使用了 lazy 以便我们可以在找到第一个元素后停止评估元素.

Here we're using compactMap(_:) in order to get a sequence of elements that were successfully cast to T, and then first to get the first element of that sequence. We're also using lazy so that we can stop evaluating elements after finding the first.

示例用法:

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).也许不像您想要的语法那么灵巧,但我认为它与您将要获得的一样好.

Note that you have to use .self in order to refer to the metatype of a specific type (in this case, SomeProtocol). Perhaps not a slick as the syntax you were aiming for, but I think it's about as good as you're going to get.

虽然在这种情况下值得注意的是,该函数最好放在Sequence的扩展中:

Although it's worth noting in this case that the function would be better placed in an extension of 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")

这篇关于在 Swift 中采用类型名称的泛型函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆