5
votes

Appeler une méthode avec un nom de classe dynamique en swift

Comment pouvons-nous appeler des fonctions de classe avec un nom de classe dynamique?

Supposons l'exemple suivant où j'ai deux classes avec des méthodes avec la même signature

class Foo{
   class func doSomething()

}

class Foobar {
   class func doSomething()
}

class ActualWork{
   //call following method with a variable type so that it accepts dynamic class  name
   func callDynamicClassMethod(x: dynamicClass)
    x.doSomething() 
  }

Comment cela être implémenté de sorte que x accepte les valeurs au moment de l'exécution

Edit: Désolé, j'ai manqué de mentionner que je cherchais d'autres moyens que l'approche orientée protocole. Il s'agit davantage d'une question exploratoire à explorer s'il existe une approche / pods / bibliothèques plus directe pour y parvenir.


1 commentaires

J'essaierais d'ajouter un protocole au Foo et Foobar avec doSomething (). Ensuite, créez un genericType qui requiert ce protocole, puis utilisez le genericType comme paramètre.


5 Réponses :


3
votes

vous pouvez y parvenir avec l'aide du protocole

 protocol common {
   static func doSomething()
}


class Foo : common{
   static  func doSomething() {
        print("Foo")
    }
}

class Foobar : common {
   static func doSomething() {
        print("Foobar")
    }
}


class ActualWork{
    //call following method with a variable type so that it accepts dynamic class  name
    func callDynamicClassMethod(x: common.Type) {
            x.doSomething()
    }
}

let fooObj : common = Foo()
let Foobarobj : common = Foobar()

let workObk = ActualWork()
workObk.callDynamicClassMethod(x:Foo.self)
workObk.callDynamicClassMethod(x:Foobar.self)


1 commentaires

La question concerne les fonctions de classe



7
votes

La programmation orientée génériques et protocole fera l'affaire:

protocol Doable {

    static func doSomething()
}

class Foo: Doable {

    static func doSomething() {
        debugPrint("Foo")
    }
}

class Foobar: Doable {

    static func doSomething() {
        debugPrint("Foobar")
    }
}

class ActualWork {

    func callDynamicClassMethod<T: Doable>(x: T.Type) {
        x.doSomething()
    }
}

let work = ActualWork()

work.callDynamicClassMethod(x: Foo.self)
work.callDynamicClassMethod(x: Foobar.self)


0 commentaires

3
votes

Je pense qu'il y a trois solutions. J'ai partagé un exemple ci-dessous.

  1. Utilisez un "protocole" qui a des exigences de fonction "doSomething ()".
  2. Créez une fonction qui obtient la définition de fonction en tant que paramètre.
  3. Utilisez la réflexion. vous pouvez utiliser EVReflection qui est une bonne API pour la réflexion.

exemple de code:

protocol FooProtocol {
    static func doSomething()
}

class Foo: FooProtocol {
    class func doSomething() {
        print("Foo:doSomething")
    }
}

class Foobar: FooProtocol {
    class func doSomething() {
        print("Foobar:doSomething")
    }
}

class ActualWork {
    func callDynamicClassMethod<T: FooProtocol>(x: T.Type) {
        x.doSomething()
    }

    func callDynamicClassMethod(x: @autoclosure () -> Void) {
        x()
    }

    func callDynamicClassMethod(x: () -> Void) {
        x()
    }
}

ActualWork().callDynamicClassMethod(x: Foo.self)
ActualWork().callDynamicClassMethod(x: Foobar.self)
print("\n")
ActualWork().callDynamicClassMethod(x: Foo.doSomething())
ActualWork().callDynamicClassMethod(x: Foobar.doSomething())
print("\n")
ActualWork().callDynamicClassMethod(x: Foo.doSomething)
ActualWork().callDynamicClassMethod(x: Foobar.doSomething)


1 commentaires

Bonnes solutions 👍



7
votes

J'ai aimé cette question, car elle m'a fait réfléchir un peu en dehors des sentiers battus.

Je vais y répondre en la divisant en quelques parties.

Premier

appeler les fonctions de classe

La fonction de classe est essentiellement une méthodes de type , ce qui peut être réalisé en utilisant le mot static dans le contexte class .

En tenant compte de cela, vous pouvez obtenir une solution simple, en utilisant protocole et en passant la référence de classe (conforme à ce protocole) comme ceci:

class ActualWork : NSObject{

    func callDynamicClassMethod<T: NSObject>(x: T.Type, methodName: String){
        x.perform(Selector(methodName));
    }

}

class Ccc : NSObject{
    @objc class func doSomething(){
        print("Ccc class Doing something ");
    }

}

class Ddd : NSObject{
    @objc class func doSomething(){
        print("Ccc class Doing something ");
    }

    @objc class func doOther(){
        print("Ccc class Doing something ");
    }
}

//This is how you can use it
func usage() {
    let aw = ActualWork();

    aw.callDynamicClassMethod(x: Ccc.self, methodName: "doSomething");
    aw.callDynamicClassMethod(x: Ddd.self, methodName: "doSomething");
    aw.callDynamicClassMethod(x: Ddd.self, methodName: "doOther");

}

Second

Si vous n'avez pas vraiment besoin de la méthode sur le contexte de classe, vous pouvez envisager d'utiliser des méthodes d'instance . Dans ce cas, la solution serait encore plus simple, comme ceci:

protocol Bbb{
  func doSomething();
}
class Bar : Bbb{
  func doSomething() {
    print("Bar instance doing something");
  }
}
class BarBar : Bbb{
  func doSomething() {
    print("BarBar instance doing something");
  }
}
class ActualWork{
  //Using instance (non-static) method
  func callDynamicInstanceMethod <T: Bbb> (x: T){
    x.doSomething();
  }
}
//This is how you can use it
func usage(){
   let aw = ActualWork();
    aw.callDynamicInstanceMethod(x: Bar());
    aw.callDynamicInstanceMethod(x: BarBar());
}

Troisième

Si vous devez utiliser le class func syntaxe, comme OP l'a fait à l'origine:

classe func doSomething ()

Vous NE POUVEZ PAS simplement utiliser un protocole. Parce que le protocole n'est pas une classe ... Donc le compilateur ne le permettra pas.

 Impossible de déclarer la fonction de classe dans le protocole

Mais c'est toujours possible, vous pouvez y parvenir en utilisant Sélecteur avec la méthode NSObject.perform p>

comme ceci:

protocol Aaa{
   static func doSomething();
}
class Foo : Aaa{
  static func doSomething() {
    print("Foo doing something");
  }
}
class FooBar : Aaa{
  static func doSomething() {
    print("FooBar doing something");
  }
}

class ActualWork{

  //Using class (static) method
  func callDynamicClassMethod <T: Aaa> (x: T.Type) {
    x.doSomething();
  }
}


//This is how you can use it
func usage(){
    let aw = ActualWork();

    aw.callDynamicClassMethod(x: Foo.self);
    aw.callDynamicClassMethod(x: Foo.self);
}


1 commentaires

Je pense que les sélecteurs sont la seule voie à suivre si l'on veut éviter les protocoles.



0
votes

Il semble que vous recherchiez saisie de canard , ce qui est plus difficile à réaliser dans un langage de type statique (à quelques exceptions près, répertorié dans la page Wikipédia liée).

En effet, l'appel dynamique d'une méthode nécessite des connaissances sur la disposition de l'objet cible, donc soit l'héritage de la classe déclarant la méthode, soit la conformité à un protocole qui nécessite cette méthode.

À partir de Swift 4.2 et l'introduction du membre dynamique recherche , il existe une autre approche pour résoudre votre problème, mais cela implique également une certaine cérémonie:

// This needs to be used as base of all classes that you want to pass
// as arguments 
@dynamicMemberLookup
class BaseDynamicClass {
    subscript(dynamicMember member: String) -> () -> Void {
        return { /* empty closure do nothing */ }
    }
}

// subclasses can choose to respond to member queries any way they like
class Foo: BaseDynamicClass {
    override subscript(dynamicMember member: String) -> () -> Void {
        if member == "doSomething" { return doSomething }
        return super[dynamicMember: member]
    }

    func doSomething() {
        print("Dynamic from Foo")
    }

}

class Bar: BaseDynamicClass {
    override subscript(dynamicMember member: String) -> () -> Void {
        if member == "doSomething" { return doSomething }
        return super[dynamicMember: member]
    }

    func doSomething() {
        print("Dynamic from Bar")
    }
}

func test(receiver: BaseDynamicClass) {
    receiver.doSomething()
}

test(receiver: Bar()) // Dynamic from Bar

Pour conclure, dans la version actuelle de Swift, il n'y a aucun moyen de avoir à la fois l'argument et la méthode dynamiques, un terrain d'entente doit être défini.


0 commentaires