检查元类型是否为具有类型为MyProtocol的元素类型的数组 [英] Checking if a metatype is an Array with an Element type of kind MyProtocol

查看:96
本文介绍了检查元类型是否为具有类型为MyProtocol的元素类型的数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要为不同的课程做不同的事情。例如,我已经创建了协议

 协议ResponseProtocol {
associatedtype ResponseType:任何
}

协议MappableProtocol {
init(map:String)
}

我添加了我的数据类 MyDto

  class MyDto:MappableProtocol {
需要init(map:String){}
}

和3个不同的响应类

  class A1:ResponseProtocol {
typealias ResponseType = String
}
$ b $ class A2:ResponseProtocol {
typealias ResponseType = MyDto
}

class A3:ResponseProtocol {
typealias ResponseType = [MyDto]
}

现在我需要根据 ResponseType

我试过这段代码,但遇到 Array s

  class API {

func test< T:ResponseProtocol>(a:T) - > String {

如果T.ResponseType.self是String.Type {
返回String
}
如果T.ResponseType.self是MappableProtocol.Type {
返回MappableProtocol
}


如果T.ResponseType.self为[Any] .Type {
returnArray< Any> //为什么这是错误的?
}
如果T.ResponseType.self是[MappableProtocol] .Type {
returnArray< MappableProtocol> //为什么这是错误的?
}
如果T.ResponseType.self是[MyDto] .Type {
returnArray< MyDto> //为什么只有这样?
}
returnnotFound
}

}
let api = API()
let t1 = api.test(a: A1())
let t2 = api.test(a:A2())
let t3 = api.test(a:A3())

在我的操场控制台中,对于数组 A3 ,我看到 Array< MyDto> ; ,但我预计数组的第一个返回值就像 Array< Any> 。 >如何检查元素元素是否为 MappableProtocol [MyDto] 可以自由转换为 [MappableProtocol] [Any] ,这些实际上只是编译器在后台执行的神奇转换(有关更多信息,请参阅本Q& A )。



元类型值不存在相同的转换,这就是为什么Sw ift表示 [MyDto] .Type 不是 [MappableProtocol] .Type 也不是 [任意] .Type - 它们是不相关的元类型类型。



可能最简单的解决方案就是忘记使用元类型,而是仅仅声明 test(a:)的不同重载来处理不同的 ResponseType

  //'default'测试的重载(a :) 
func测试< T:ResponseProtocol>( a:T) - > String {
returnnotFound
}

func test< T:ResponseProtocol>(a:T) - > String where T.ResponseType == String {
returnString
}

func test< T:ResponseProtocol>(a:T) - > String where T.ResponseType:MappableProtocol {
returnMappableProtocol
}

func test< T:ResponseProtocol>(a:T) - > String where T.ResponseType == [MyDto] {
returnArray< MDto>
}

//接受符合ResponseProtocol的类型的测试(a :)的重载,其中
// ResponseType是具有任意Element类型的数组。
func test< T:ResponseProtocol,ResponseTypeElement:MappableProtocol>(a:T) - >字符串
其中T.ResponseType == [ResponseTypeElement]
{
返回Array< MappableProtocol>

$ b $ print(test(a:A1()))// String
print(test(a:A2()))// MappableProtocol
print (test(a:A3()))// Array< MyDto>

//一个符合MappableProtocol,
//但不是MyDto的ResponseType的MappableProtocol。
class Foo:MappableProtocol {required init(map:String){}}
class A4:ResponseProtocol {typealias ResponseType = [Foo]}

print(test(a:A4 ()))// Array< MappableProtocol>

(我删除了您的 API 类只是为了简化事情)



编译器将简单地解决在编译时调用哪个重载,而不是让运行时跳过大量类型转换






如果您坚持处理元类型值,一种可能的解决方案是定义 Array 符合的虚拟协议 >类似的Q& A ),然后我们可以将元类型值转换为。然后我们可以声明一个 elementType 静态需求,以便提取数组元素。 self metatype value,然后我们可以检查其类型以确定数组可以转换为什么。 b

例如,如果我们定义并符合 Array _ArrayProtocol

  protocol _ArrayProtocol {
static var elementType:Any.Type {get}
}

扩展Array:_ArrayProtocol {
static var elementType: Any.Type {
return Element.self
}
}

现在我们可以使用 test(a:)这样:

  func test< T:ResponseProtocol>(a:T) - > String {

如果T.ResponseType.self是String.Type {
返回String
}

如果T.ResponseType.self是MappableProtocol .Type {
returnMappableProtocol
}

//尝试将T.ResponseType.self元类型值转换为存在元元类型
//类型_ArrayProtocol。类型(即符合_ArrayProtocol的类型),
//在这种情况下,这只是Array。
如果让responseType = T.ResponseType.self为? _ArrayProtocol.Type {

//开启元素类型,尝试转换为不同的元类型类型。
switch responseType.elementType {
case MyDto.Type:
returnArray< MyDto>
case是MappableProtocol.Type:
返回Array< MappableProtocol>
默认值:
返回Array< Any>



returnnotFound
}

print(test(a:A1()))// String
print(test(a:A2()))// MappableProtocol
print(test(a:A3()))// Array< MyDto>
print(test(a:A4()))// Array< MappableProtocol>


I need do different things for different classes. For example I've created the protocols

protocol ResponseProtocol {
    associatedtype ResponseType: Any
}

protocol MappableProtocol {
    init(map: String)
}

and I'm adding my data class MyDto

class MyDto: MappableProtocol {
    required init(map: String) { }
}

and 3 different response classes

class A1: ResponseProtocol {
    typealias ResponseType = String
}

class A2: ResponseProtocol {
    typealias ResponseType = MyDto
}

class A3: ResponseProtocol {
    typealias ResponseType = [MyDto]
}

now I need do different things depending on ResponseType.

I tried this code, but I'm running into problems with Arrays

class API {

    func test<T: ResponseProtocol>(a: T) -> String {

        if T.ResponseType.self is String.Type {
            return "String"
        }
        if T.ResponseType.self is MappableProtocol.Type {
            return "MappableProtocol"
        }


        if T.ResponseType.self is [Any].Type {
            return "Array<Any>" // Why is this false?
        }
        if T.ResponseType.self is [MappableProtocol].Type {
            return "Array<MappableProtocol>" //Why is this false?
        }
        if T.ResponseType.self is [MyDto].Type {
            return "Array<MyDto>" // Why is only this true?
        }
        return "notFound"
    }

}
let api = API()
let t1 = api.test(a: A1())
let t2 = api.test(a: A2())
let t3 = api.test(a: A3())

In my playground console, for array A3, I see Array<MyDto>, but I expected the first return for array like Array<Any>.

How can I check for an Array where the Element is of kind MappableProtocol?

解决方案

The problem is that while instances of [MyDto] can be freely converted to [MappableProtocol] and [Any], these are really just magical conversions that the compiler does behind the scenes (see this Q&A for more information).

The same conversions don't exist for metatype values, which is why Swift says that a [MyDto].Type is not a [MappableProtocol].Type nor a [Any].Type – they are unrelated metatype types.

Likely the simplest solution in your case would just be to forget working with metatypes, and instead just declare different overloads of test(a:) to handle different ResponseType types.

// 'default' overload of test(a:)
func test<T : ResponseProtocol>(a: T) -> String {
    return "notFound"
}

func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType == String {
    return "String"
}

func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType : MappableProtocol {
    return "MappableProtocol"
}

func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType == [MyDto] {
    return "Array<MDto>"
}

// overload of test(a:) that accepts a type that conforms to ResponseProtocol, where the
// ResponseType is an Array with arbitrary Element type.
func test<T : ResponseProtocol, ResponseTypeElement : MappableProtocol>(a: T) -> String
    where T.ResponseType == [ResponseTypeElement]
{
    return "Array<MappableProtocol>"
}

print(test(a: A1())) // String
print(test(a: A2())) // MappableProtocol
print(test(a: A3())) // Array<MyDto>

// A MappableProtocol with a ResponseType that conforms to MappableProtocol,
// but isn't MyDto.
class Foo : MappableProtocol { required init(map: String) { } }
class A4  : ResponseProtocol { typealias ResponseType = [Foo] }

print(test(a: A4())) // Array<MappableProtocol>

(I removed your API class just to simplify things)

The compiler will simply resolve which overload to call at compile time, rather than having the runtime jump through lots of type-casting hoops.


If you insist on working with metatype values, one possible solution is to define a dummy protocol for Array to conform to (see for example this similar Q&A), which we can then cast the metatype values to. We can then declare an elementType static requirement in order to extract the Array's Element.self metatype value, which we can then inspect the type of in order to determine what the array is convertible to.

For example, if we define and conform Array to _ArrayProtocol:

protocol _ArrayProtocol {
    static var elementType: Any.Type { get }
}

extension Array : _ArrayProtocol {
    static var elementType: Any.Type {
        return Element.self
    }
}

We can now use test(a:) like so:

func test<T : ResponseProtocol>(a: T) -> String {

    if T.ResponseType.self is String.Type {
        return "String"
    }

    if T.ResponseType.self is MappableProtocol.Type {
        return "MappableProtocol"
    }

    // attempt to cast the T.ResponseType.self metatype value to the existential metatype
    // type _ArrayProtocol.Type (i.e a type that conforms to _ArrayProtocol),
    // in this case, that's only ever Array.
    if let responseType = T.ResponseType.self as? _ArrayProtocol.Type {

        // switch on the element type, attempting to cast to different metatype types.
        switch responseType.elementType {
        case is MyDto.Type:
            return "Array<MyDto>"
        case is MappableProtocol.Type:
            return "Array<MappableProtocol>"
        default:
            return "Array<Any>"
        }
    }

    return "notFound"
}

print(test(a: A1())) // String
print(test(a: A2())) // MappableProtocol
print(test(a: A3())) // Array<MyDto>
print(test(a: A4())) // Array<MappableProtocol>

这篇关于检查元类型是否为具有类型为MyProtocol的元素类型的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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