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

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

问题描述

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

  public T f< T>() 
{
返回T
}

var x = f< string>()

Swift不允许您在调用泛型方法时专门化它。编译器希望依赖类型推断,所以这是不可能的:

  func f< T> ;() - > T' {
返回T的东西?


让x = f< String>()//在Swift中不允许

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

但它不适合我想做的事情:
$ b

  let x = f()作为字符串? 

编辑(澄清)



我可能并不十分清楚问题实际是什么,它只是用于调用返回给定类型(任何类型)的函数的简单语法。作为一个简单的例子,假设你有一个Any的数组,并且你创建了一个返回给定类型的第一个元素的函数:$ b $
$ b

b

//返回该类型数组中的第一个元素
func findFirst< T>(array:[Any ]) - > T' {
return array.filter(){$ 0 is T} .first as? T

$ / code>

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

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

let x = findFirst (数组)作为字符串?

这很简单,但是如果返回的类型是某个带有方法的协议并且您想调用返回对象的方法:
$ b

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

语法只是尴尬。在C#中(与Swift类似),您可以这样做:

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

不幸的是,这在Swift中是不可能的。



<所以问题是:有没有一种方法可以用更干净(不太笨拙的)语法来实现这一点。

不幸的是,你不能明确地定义泛型函数的类型(通过使用< ...> >语法)。但是,您可以 为该函数提供一个通用的元类型( T.Type )作为参数 Swift推断函数的泛型类型,正如 Roman说的


$ b $对于你的具体例子,你会希望你的函数看起来像这样:

pre $ func findFirst< T> (在数组中:[Any],ofType _:T.Type) - > T' {
返回array.lazy.flatMap {$ 0 as? T} .first
}

这里我们使用 flatMap (_:),以获得成功转换为 T ,然后第一获取该序列的第一个元素。我们还使用 lazy ,以便在找到第一个元素后停止评估元素。

 协议SomeProtocol {
func doSomething()
}

协议AnotherProtocol {
func somethingElse()
}

扩展字符串:SomeProtocol {
func doSomething(){
print(success:,self)



让a:[Any] = [5,str,6.7]

//输出success:str,as第二个元素可以转换成SomeProtocol。
findFirst(in:a,ofType:SomeProtocol.self)?。doSomething()

//不输出任何内容,因为没有任何元素符合AnotherProtocol。
findFirst(in:a,ofType:AnotherProtocol.self)?somethingElse()

请注意,您必须使用 .self 来引用特定类型的元类型(在这种情况下, SomeProtocol )。也许并不是你想要的语法,但我认为它和你将要获得的一样好。



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

 扩展名序列{
func first< T>(ofType _:T.Type) - > T' {
//很遗憾,我们不能轻易使用lazy.flatMap {$ 0 as? T} .first
//在这里,因为LazyMapSequence没有'first'属性(我们不得不
//获取迭代器并调用next(),但是在那时我们可能会以及
//做一个for循环)
for self {
if let element = element as? T {
返回元素
}
}
返回零
}
}

让a:[Any] = [ 5,str,6.7]
print(a.first(ofType:String.self)as Any)//可选(str)


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 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?

EDIT (CLARIFICATION)

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).

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()

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

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

Sadly, that's not possible in Swift.

So the question is: is there a way to accomplish this with a cleaner (less awkward) syntax.

解决方案

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.flatMap{ $0 as? T }.first
}

Here we're using flatMap(_:) 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.

Example usage:

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()

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.

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.flatMap { $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天全站免登陆