如何将类类型作为函数参数传递 [英] How to pass a class type as a function parameter

查看:448
本文介绍了如何将类类型作为函数参数传递的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个通用函数调用Web服务并将JSON响应序列化回一个对象。

  class func invokeService< ; T>(service:String,withParams params:Dictionary< String,String> ;, returningClass:AnyClass,completionHandler handler:((T) - >())){

/ *构造URL ,调用服务并解析响应* /
}

我试图完成就是相当于这个Java代码

  public< T> T invokeService(final String serviceURLSuffix,final Map< String,String> params,
final Class< T> classTypeToReturn){
}




  • 我的方法签名是否正确?

  • 更具体地说, code> AnyClass 作为参数类型
    正确的做法

  • 调用该方法时,我将 MyObject.self 作为returningClass值,但我得到一个编译错误无法将表达式的类型'()'转换为键入'String'





  CastDAO.invokeService(test,withParams:[test :test],returningClass:CityInfo.self){cityInfo in /*...*/ 

}






编辑

code> object_getClass ,就像holex提到的那样,但现在我得到了:

错误:需要做什么才能遵守协议?

  class CityInfo:NSObject {

var cityName :字符串?
var regionCode:String?
var regionName:String?


解决方案

您正在接近错误的方式:在Swift中,与Objective-C不同,类有特定的类型,甚至有一个继承层次结构(也就是说,如果类 B 继承,然后 B.Type 也继承自 A.Type ):

  class A {} 
class B:A {}
class C {}

// B继承自A
let object:A = B()

// B.Type也继承自A.Type
let type:A.Type = B. self

//错误:'C'不是'A'的子类型
let type2:A.Type = C.self

这就是为什么你不应该使用 AnyClass ,除非你真的想允许任何类。在这种情况下,正确的类型将是 T.Type ,因为它表示 returningClass 参数与参数实际上,使用它而不是 AnyClass 可以让编译器正确地推断出这些类型的类型。



方法调用:
$ b

  class func invokeService< T>(service:String,withParams params:Dictionary< String,String> ;, returningClass: T.Type,completionHandler处理函数:((T) - >())){
//编译器正确地推断出T是returnedClass
handler(returningClass())$的实例的类




现在有一个构造 T 传递给处理程序:如果您现在尝试运行代码,编译器会抱怨 T 不能用()构造。正确的是: T 必须被明确地约束以要求它实现一个特定的初始化器。



这可以是使用如下协议完成:

  protocol Initable {
init()
}

class CityInfo:NSObject,Initable {
var cityName:String?
var regionCode:String?
var regionName:String?

//在这里没什么改变,CityInfo已经实现了init()
}

然后,您只需将 invokeService 的通用约束从< T> 更改为< T:Initable>



小费



你会得到奇怪的错误,比如无法将表达式的类型'()'转换为键入'String',将方​​法调用的每个参数移动到它自己的变量通常都是有用的。它有助于缩小导致错误的代码并揭示类型推断问题:

  let service =test
let params = [test:test]
let returningClass = CityInfo.self

CastDAO.invokeService(service,withParams:params,returningClass:returningClass){cityInfo in / * ... * /

}

现在有两种可能性:错误移动到其中一个变量(这意味着错误的部分在那里),或者你得到一个神秘的信息,比如不能将表达式的类型()转换为键入



编译器无法推断出你写的东西的类型。在这种情况下,问题在于 T 仅用于闭包的参数,并且您传递的闭包并不表示任何特定的类型,因此编译器不知道什么类型来推断。通过将 returningClass 的类型更改为包含 T ,您可以为编译器提供一种确定泛型参数的方法。


I have a generic function that calls a web service and serialize the JSON response back to an object.

class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: AnyClass, completionHandler handler: ((T) -> ())) {

            /* Construct the URL, call the service and parse the response */
}

What I'm trying to accomplish is is the equivalent of this Java code

public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
                               final Class<T> classTypeToReturn) {
}

  • Is my method signature for what I'm trying to accomplish correct?
  • More specifically, is specifying AnyClass as a parameter type the right thing to do?
  • When calling the method, I'm passing MyObject.self as the returningClass value, but I get a compilation error "Cannot convert the expression's type '()' to type 'String'"

CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/

}


Edit:

I tried using object_getClass, as mentioned by holex, but now I get:

error: "Type 'CityInfo.Type' does not conform to protocol 'AnyObject'"

What need to be done to conform to the protocol?

class CityInfo : NSObject {

    var cityName: String?
    var regionCode: String?
    var regionName: String?
}

解决方案

You are approaching it in the wrong way: in Swift, unlike Objective-C, classes have specific types and even have an inheritance hierarchy (that is, if class B inherits from A, then B.Type also inherits from A.Type):

class A {}
class B: A {}
class C {}

// B inherits from A
let object: A = B()

// B.Type also inherits from A.Type
let type: A.Type = B.self

// Error: 'C' is not a subtype of 'A'
let type2: A.Type = C.self

That's why you shouldn't use AnyClass, unless you really want to allow any class. In this case the right type would be T.Type, because it expresses the link between the returningClass parameter and the parameter of the closure.

In fact, using it instead of AnyClass allows the compiler to correctly infer the types in the method call:

class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: T.Type, completionHandler handler: ((T) -> ())) {
    // The compiler correctly infers that T is the class of the instances of returningClass
    handler(returningClass())
}

Now there's the problem of constructing an instance of T to pass to handler: if you try and run the code right now the compiler will complain that T is not constructible with (). And rightfully so: T has to be explicitly constrained to require that it implements a specific initializer.

This can be done with a protocol like the following one:

protocol Initable {
    init()
}

class CityInfo : NSObject, Initable {
    var cityName: String?
    var regionCode: String?
    var regionName: String?

    // Nothing to change here, CityInfo already implements init()
}

Then you only have to change the generic constraints of invokeService from <T> to <T: Initable>.

Tip

If you get strange errors like "Cannot convert the expression's type '()' to type 'String'", it is often useful to move every argument of the method call to its own variable. It helps narrowing down the code that is causing the error and uncovering type inference issues:

let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self

CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/

}

Now there are two possibilities: the error moves to one of the variables (which means that the wrong part is there) or you get a cryptic message like "Cannot convert the expression's type () to type ($T6) -> ($T6) -> $T5".

The cause of the latter error is that the compiler is not able to infer the types of what you wrote. In this case the problem is that T is only used in the parameter of the closure and the closure you passed doesn't indicate any particular type so the compiler doesn't know what type to infer. By changing the type of returningClass to include T you give the compiler a way to determine the generic parameter.

这篇关于如何将类类型作为函数参数传递的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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