一尘不染

如何将通用协议用作变量类型

swift

假设我有一个协议:

public protocol Printable {
    typealias T
    func Print(val:T)
}

这是实现

class Printer<T> : Printable {

    func Print(val: T) {
        println(val)
    }
}

我的期望是我必须能够使用Printable变量来打印这样的值:

let p:Printable = Printer<Int>()
p.Print(67)

编译器抱怨此错误:

“协议’Printable’只能用作一般约束,因为它具有Self或关联的类型要求”

难道我做错了什么 ?有任何解决这个问题的方法吗 ?

**EDIT :** Adding similar code that works in C#

public interface IPrintable<T> 
{
    void Print(T val);
}

public class Printer<T> : IPrintable<T>
{
   public void Print(T val)
   {
      Console.WriteLine(val);
   }
}


//.... inside Main
.....
IPrintable<int> p = new Printer<int>();
p.Print(67)

编辑2:我想要的真实世界的例子。请注意,这不会编译,但是会介绍我想要实现的目标。

protocol Printable 
{
   func Print()
}

protocol CollectionType<T where T:Printable> : SequenceType 
{
   .....
   /// here goes implementation
   ..... 
}

public class Collection<T where T:Printable> : CollectionType<T>
{
    ......
}

let col:CollectionType<Int> = SomeFunctiionThatReturnsIntCollection()
for item in col {
   item.Print()
}

阅读 239

收藏
2020-07-07

共1个答案

一尘不染

正如Thomas所指出的,您可以通过根本不提供类型来声明变量(或者可以将其显式地指定为type
Printer<Int>。但这是对为什么不能拥有Printable协议类型的解释。

您不能将协议类型与常规协议一样对待,也不能将它们声明为独立变量类型。要考虑原因,请考虑这种情况。假设您声明了一个协议,用于存储任意类型,然后将其取回:

// a general protocol that allows for storing and retrieving
// a specific type (as defined by a Stored typealias
protocol StoringType {
    typealias Stored

    init(_ value: Stored)
    func getStored() -> Stored
}

// An implementation that stores Ints
struct IntStorer: StoringType {
    typealias Stored = Int
    private let _stored: Int
    init(_ value: Int) { _stored = value }
    func getStored() -> Int { return _stored }
}

// An implementation that stores Strings
struct StringStorer: StoringType {
    typealias Stored = String
    private let _stored: String
    init(_ value: String) { _stored = value }
    func getStored() -> String { return _stored }
}

let intStorer = IntStorer(5)
intStorer.getStored() // returns 5

let stringStorer = StringStorer("five")
stringStorer.getStored() // returns "five"

好的,到目前为止很好。

现在,将变量的类型设为类型实现的协议而不是实际类型的主要原因是,您可以将所有都符合该协议的不同类型的对象分配给同一变量,并获得多态性运行时的行为取决于对象实际是什么。

但是,如果协议具有关联的类型,则无法执行此操作。以下代码在实践中将如何工作?

// as you've seen this won't compile because
// StoringType has an associated type.

// randomly assign either a string or int storer to someStorer:
var someStorer: StoringType = 
      arc4random()%2 == 0 ? intStorer : stringStorer

let x = someStorer.getStored()

在上面的代码中,类型x是什么?安Int?还是一个String?在Swift中,所有类型都必须在编译时固定。函数无法根据运行时确定的因素动态地从一种类型返回到另一种类型。

相反,您只能将其StoredType用作通用约束。假设您要打印出任何类型的存储类型。您可以编写如下函数:

func printStoredValue<S: StoringType>(storer: S) {
    let x = storer.getStored()
    println(x)
}

printStoredValue(intStorer)
printStoredValue(stringStorer)

可以,因为在编译时,好像编译器写出了两个版本printStoredValue:一个用于Ints,一个用于Strings。在这两个版本中,x已知是特定类型的。

2020-07-07