将协议和符合类(!)实例作为参数的函数 [英] Function that takes a protocol and a conforming class (!) instance as parameters

查看:99
本文介绍了将协议和符合类(!)实例作为参数的函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图弄清楚如何定义一个函数,该函数需要以下
两个参数:


  1. 协议。

  2. 符合该协议的 class (a 引用类型)的实例。 ol>

    例如,给定

     协议P {} 
    class C:P {} // Class,符合P
    class D {} // Class,不符合P
    struct E:P {} //符合P

    这应该编译:

      register(P.self,obj:C())//(1)

    不应该编译:

      register(P.self,obj:D())//(2)D不符合P 
    寄存器(P.self,obj:E())//(3)E不是类

    如果我们放弃第二个参数是一个类实例的条件很容易:

      func寄存器< T(原型:T.Type,obj:T){
    // ...
    }

    但是这会接受struct(value type)在(3)中也是如此。
    这看起来很有希望并且编译


      func寄存器< T:AnyObject>(proto:T.Type,obj:T ){
    // ...
    }

    但没有(1)(2)(3)编译(例如



      register(P.self,obj:C())//(1)
    //错误:无法用类型为'(P.Protocol,obj:C)'的参数列表'调用'register''

    我假设编译器错误的原因与
    相同

    另一次失败的尝试是

      func寄存器< T>(原型:T.Type,obj:协议< T,AnyObject>){} 
    //错误:非协议类型'T'不能用于'protocol< ...>'

    一个可行的替代方案是一个函数,它Ť akes作为参数


    1. 类协议。
    2. 一种符合该协议的类型。

    这个问题是如何限制第一个参数,使得只有
    类协议公认。


    背景:最近我偶然发现了
    SwiftNotificationCenter
    实现面向协议的类型安全通知机制的项目。
    它有一个
    register
    方法如下所示:

      public class NotificationCenter {

    public static func register< T>(protocolType:T.Type,observer:T){
    guard let object = observer as? AnyObject其他{
    fatalError(期待引用类型,但发现值类型:\(观察者))
    }

    // ...
    }

    // ...
    }

    观察者被储存作为弱引用,这就是为什么他们
    必须是引用类型,即类的实例。
    但是,这只在运行时检查,我不知道如何进行编译时检查。



    我是否缺少简单/明显的东西?

    解决方案

    你不能直接做你想做的事情。它与引用类型无关,这是因为任何约束都会导致 T 存在,因此当您引用协议的元类型<$时,无法在调用站点满足它们c $ c> P.self:P.Protocol 和采用者 C 。有一个特殊情况,当 T 不受限制时,允许它首先工作。



    到目前为止更常见的情况是限制 T:P 并且要求 P:class ,因为只是 注册< T>(原型:Any.Type,obj:T),它可以完成所有的工作。理论上Swift可以支持对元类型的约束,ala register< T:AnyObject,U:AnyProtocol,其中T.Type:U>(原型: U,obj:T)但我怀疑它会在很多情况下有用。


    I am trying to figure out how to define a function which takes the following two parameters:

    1. A protocol.
    2. An instance of a class (a reference type) conforming to that protocol.

    For example, given

    protocol P { }
    class C : P { } // Class, conforming to P
    class D { }     // Class, not conforming to P
    struct E: P { } // Struct, conforming to P
    

    this should compile:

    register(P.self, obj: C()) // (1)
    

    but these should not compile:

    register(P.self, obj: D()) // (2)  D does not conform to P
    register(P.self, obj: E()) // (3)  E is not a class
    

    It is easy if we drop the condition that the second parameter is a class instance:

    func register<T>(proto: T.Type, obj: T) {
        // ...
    }
    

    but this would accept the struct (value type) in (3) as well. This looked promising and compiles

    func register<T: AnyObject>(proto: T.Type, obj: T) {
        // ...
    }
    

    but then none of (1), (2), (3) compile anymore, e.g.

    register(P.self, obj: C()) // (1)
    // error: cannot invoke 'register' with an argument list of type '(P.Protocol, obj: C)'
    

    I assume that the reason for the compiler error is the same as in Protocol doesn't conform to itself?.

    Another failed attempt is

    func register<T>(proto: T.Type, obj: protocol<T, AnyObject>) { }
    // error: non-protocol type 'T' cannot be used within 'protocol<...>'
    

    A viable alternative would be a function which takes as parameters

    1. A class protocol.
    2. An instance of a type conforming to that protocol.

    Here the problem is how to restrict the first parameter such that only class protocols are accepted.

    Background: I recently stumbled over the SwiftNotificationCenter project which implements a protocol-oriented, type safe notification mechanism. It has a register method which looks like this:

    public class NotificationCenter {
    
        public static func register<T>(protocolType: T.Type, observer: T) {
            guard let object = observer as? AnyObject else {
                fatalError("expecting reference type but found value type: \(observer)")
            }
    
            // ...
        }
    
        // ...
    }
    

    The observers are then stored as weak references, and that's why they must be reference types, i.e. instances of a class. However, that is checked only at runtime, and I wonder how to make it a compile-time check.

    Am I missing something simple/obvious?

    解决方案

    You can't do what you are trying to do directly. It has nothing to do with reference types, it's because any constraints make T existential so it is impossible to satisfy them at the call site when you're referencing the protocol's metatype P.self: P.Protocol and an adopter C. There is a special case when T is unconstrained that allows it to work in the first place.

    By far the more common case is to constrain T: P and require P: class because just about the only thing you can do with an arbitrary protocol's metatype is convert the name to a string. It happens to be useful in this narrow case but that's it; the signature might as well be register<T>(proto: Any.Type, obj: T) for all the good it will do.

    In theory Swift could support constraining to metatypes, ala register<T: AnyObject, U: AnyProtocol where T.Type: U>(proto: U, obj: T) but I doubt it would be useful in many scenarios.

    这篇关于将协议和符合类(!)实例作为参数的函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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