如何使用泛型协议作为变量类型 [英] How to use generic protocol as a variable type

查看:126
本文介绍了如何使用泛型协议作为变量类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个协议:

 公共协议可打印{
typealias T
func打印(val:T)
}

这里是实现

  class Printer< T> :可打印{

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

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

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

编译器抱怨这个错误:


协议Printable只能用作通用约束条件,因为
具有自我或相关类型要求


上午我做错了什么?无论如何解决这个问题?

  **编辑:**添加类似的代码在C#中

公共接口IPrintable< T>
{
void Print(T val);
}

公共类打印机< T> :IPrintable< T>
{
public void Print(T val)
{
Console.WriteLine(val);
}
}


// ....主内
.....
IPrintable< int> p =新的打印机< int>();
p.Print(67)

编辑2:我想要的真实世界示例。请注意,这不会编译,但提出了我想实现的目标。

 协议可打印
{
func Print()
}

协议CollectionType< T其中T:可打印> :SequenceType
{
.....
///在这里执行
.....
}

公共类集合< T,其中T:可打印> :CollectionType< T>
{
......
}

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


<正如Thomas指出的那样,你可以通过不给定类型来声明你的变量(或者你可以明确地把它作为类型 Printer<< Int> 。但是这里解释了为什么你不能使用 Printable 协议。



你不能将协议与像常规协议那样的关联类型对待,并将它们声明为独立的变量类型想想为什么,考虑这个场景假设你声明了一个协议来存储一些任意类型然后取回它:

  //一个允许存储和检索
//一个特定类型的通用协议(由a存储类型
存储类型{
typealias存储

init(_ value:存储)
func getStored() - >存储
}

//执行它存储了Ints $ b $ struct IntStorer:StoringType {
typealias Stored = Int
private let _stored:Int
init(_ value:Int){_stored = value}
func getStored() - > Int {return _stored}
}

//存储字符串的实现$ b $ struct struct StringStorer:StoringType {
typealias Stored = String
private let _stored:字符串
init(_ value:String){_stored = value}
func getStored() - > String {return _stored}
}

let intStorer = IntStorer(5)
intStorer.getStored()//返回5

let stringStorer = StringStorer (five)
stringStorer.getStored()//返回five

OK ,到目前为止这么好。



现在,您将变量类型变为类型实现的协议而不是实际类型的主要原因是,你可以将所有符合该协议的不同类型的对象分配给同一个变量,并在运行时根据对象的实际情况获取多态行为。



但是你可以如果协议有关联的类型,请不要这样做。

  //如你所见,这将不会被编译,因为
// StoringType有一个关联的类型。

//随机分配一个字符串或int storer到someStorer:
var someStorer:StoringType =
arc4random()%2 == 0? intStorer:stringStorer

let x = someStorer.getStored()

上面的代码中, x 的类型是什么?一个 Int ?或字符串?在Swift中,所有类型都必须在编译时修复。根据运行时确定的因素,函数不能动态地从一种类型返回到另一种类型。



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

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

printStoredValue(intStorer)
printStoredValue(stringStorer)

这是好的,因为在编译时,就好像编译器写出两个版本的 printStoredValue :一个用于 Int s,另一个用于 String s。在这两个版本中,已知 x 是特定类型。


Let's say I have a protocol :

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

And here is the implementation

class Printer<T> : Printable {

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

My expectation was that I must be able to use Printable variable to print values like this :

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

Compiler is complaining with this error :

"protocol 'Printable' can only be used as a generic constraint because it has Self or associated type requirements"

Am I doing something wrong ? Anyway to fix this ?

**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)

EDIT 2: Real world example of what I want. Note that this will not compile, but presents what I want to achieve.

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

解决方案

As Thomas points out, you can declare your variable by not giving a type at all (or you could explicitly give it as type Printer<Int>. But here's an explanation of why you can't have a type of the Printable protocol.

You can't treat protocols with associated types like regular protocols and declare them as standalone variable types. To think about why, consider this scenario. Suppose you declared a protocol for storing some arbitrary type and then fetching it back:

// 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"

OK, so far so good.

Now, the main reason you would have a type of a variable be a protocol a type implements, rather than the actual type, is so that you can assign different kinds of object that all conform to that protocol to the same variable, and get polymorphic behavior at runtime depending on what the object actually is.

But you can't do this if the protocol has an associated type. How would the following code work in practice?

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

In the above code, what would the type of x be? An Int? Or a String? In Swift, all types must be fixed at compile time. A function cannot dynamically shift from returning one type to another based on factors determined at runtime.

Instead, you can only use StoredType as a generic constraint. Suppose you wanted to print out any kind of stored type. You could write a function like this:

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

printStoredValue(intStorer)
printStoredValue(stringStorer)

This is OK, because at compile time, it's as if the compiler writes out two versions of printStoredValue: one for Ints, and one for Strings. Within those two versions, x is known to be of a specific type.

这篇关于如何使用泛型协议作为变量类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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