当前位置: 首页 > 面试题库 >

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

邢永安
2023-03-14
问题内容

假设我有一个协议:

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

问题答案:

正如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已知是特定类型的。



 类似资料:
  • 由于TCP是基于流的,客户端发送的请求数据是像水流一样流入到服务端,服务端探测到有数据到来后应该检查数据是否是完整的,因为可能只是一个请求的部分数据到达服务端,甚至可能是多个请求连在一起到达服务端。如何判断请求是否全部到达或者从多个连在一起的请求中分离请求,就需要规定一套通讯协议。 在WorkerMan中为什么要制定协议? 传统PHP开发都是基于Web的,基本上都是HTTP协议,HTTP协议的解析

  • 由于TCP是基于流的,客户端发送的请求数据是像水流一样流入到服务端,服务端探测到有数据到来后应该检查数据是否是完整的,因为可能只是一个请求的部分数据到达服务端,甚至可能是多个请求连在一起到达服务端。如何判断请求是否全部到达或者从多个连在一起的请求中分离请求,就需要规定一套通讯协议。 在WorkerMan中为什么要制定协议? 传统PHP开发都是基于Web的,基本上都是HTTP协议,HTTP协议的解析

  • 我必须将一个协议作为参数传递给一个函数。协议是泛型的,具有关联的类型。我找不到一个好办法。 我想要实现的基本上是在JSON中存储一个键值对。所以我有一个通用的数据类型协议,它设置/获取值(根据数据类型设置和获取) 在另一个协议中,我希望将这个泛型数据类型协议作为函数参数传递。 在下面的代码中,我得到的协议“dataType”只能用作泛型约束,因为它有自身或关联的类型要求

  • 问题内容: 我正在尝试将JavaScript变量作为PHP变量包含在PHP代码中,但是这样做有问题。单击按钮时,将调用以下功能: 可能吗? 问题答案: PHP在服务器端运行。JavaScript在请求页面的用户的浏览器中在客户端运行。到执行JavaScript时,服务器上都无法访问PHP。请阅读本文,了解有关客户端和服务器端编码的详细信息。 简而言之,这是什么: 您在办公桌下方的计算机上的浏览器中

  • 问题内容: 我的应用程序有一个用于详细视图控制器的协议,指出它们必须具有一个属性: 我也有一些实现协议的不同类: 我的主视图控制器需要一个可以设置为实现该协议的任何子类的属性。 不幸的是,我找不到有关如何执行此操作的任何文档。在Objective-C中,这是微不足道的: 看来,Swift中没有可用的语法来做到这一点。我最接近的是在类定义中声明一个泛型: 但是然后我得到一个错误,说“类’Master

  • 问题内容: 我有一个协议,该协议继承自另一个协议,并且满足扩展要求。 还有另一种协议,其要求()应该为。 现在,如果我尝试使用as ,那么它将无法编译。它说, 推断的类型“地址”(通过匹配要求“ valueForDetail”)无效:不符合“ Validator”。 这种用法非法吗?我们不能像所有的那样用它代替。 下面是我正在尝试的代码。 更新: 提交了一个错误。 问题答案: David已经提到的