Arrays of Generics in Swift

前端 未结 5 1205
有刺的猬
有刺的猬 2020-12-13 06:05

I\'ve been playing around with arrays of generic classes with different types. It\'s easiest to explain my problem with some sample code:

// Obviously a very         


        
5条回答
  •  渐次进展
    2020-12-13 06:28

    There is a way - sort of - to do what you want - kind of. There is a way, with protocols, to eliminate the type restriction and still get the result that you want, kind of, but it isn't always pretty. Here is what I came up with as a protocol in your situation:

    protocol MyProtocol {
        func getValue() -> Self 
    }
    
    extension Int: MyProtocol {
        func getValue() -> Int {
            return self
        }
    }
    
    extension Double: MyProtocol {
        func getValue() -> Double {
            return self
        }
    }
    

    Note that the value property that you originally put in your protocol declaration has been changed to a method that returns the object.

    That's not very interesting.

    But now, because you've gotten rid of the value property in the protocol, MyProtocol can be used as a type, not just as a type constraint. Your Container class doesn't even need to be generic anymore. You can declare it like this:

    class Container {
        var values: [MyProtocol]
    
        init(_ values: MyProtocol...) {
            self.values = values
        }
    
        func myMethod() -> [MyProtocol] {
            return values
        }
    }
    

    And because Container is no longer generic, you can create an Array of Containers and iterate through them, printing the results of the myMethod() method:

    var containers = [Container]()
    
    containers.append(Container(1, 4, 6, 2, 6))
    containers.append(Container(1.2, 3.5))
    
    for container in containers {
        println(container.myMethod())
    }
    
    //  Output: [1, 4, 6, 2, 6]
    //          [1.2, 3.5]
    

    The trick is to construct a protocol that only includes generic functions and places no other requirements on a conforming type. If you can get away with doing that, then you can use the protocol as a type, and not just as a type constraint.

    And as a bonus (if you want to call it that), your array of MyProtocol values can even mix different types that conform to MyProtocol. So if you give String a MyProtocol extension like this:

    extension String: MyProtocol {
        func getValue() -> String {
            return self
        }
    }
    

    You can actually initialize a Container with mixed types:

    let container = Container(1, 4.2, "no kidding, this works")
    

    [Warning - I am testing this in one of the online playgrounds. I haven't been able to test it in Xcode yet...]

    Edit:

    If you still want Container to be generic and only hold one type of object, you can accomplish that by making it conform to its own protocol:

    protocol ContainerProtocol {
        func myMethod() -> [MyProtocol]
    }
    
    class Container: ContainerProtocol {
        var values: [T] = []
    
        init(_ values: T...) {
            self.values = values
        } 
    
        func myMethod() -> [MyProtocol] {
            return values.map { $0 as MyProtocol }
        }
    }
    

    Now you can still have an array of [ContainerProtocol] objects and iterate through them invoking myMethod():

    let containers: [ContainerProtocol] = [Container(5, 3, 7), Container(1.2, 4,5)]
    
    for container in containers {
        println(container.myMethod())
    }
    

    Maybe that still doesn't work for you, but now Container is restricted to a single type, and yet you can still iterate through an array of ContainterProtocol objects.

提交回复
热议问题