Вызовите метод с динамическим именем класса в swift - PullRequest
0 голосов
/ 06 февраля 2019

Как мы можем вызывать функции класса с динамическим именем класса?

Предположим, что в следующем примере у меня есть два класса с методами с одинаковой сигнатурой

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

Как это можно реализовать такчто x принимает значения во время выполнения

Редактировать: Извините, я упустил упомянуть, что я искал любые другие способы, кроме подхода, ориентированного на протокол.Это скорее исследовательский вопрос, чтобы выяснить, есть ли более прямой подход / библиотеки / библиотеки для достижения этой цели.

Ответы [ 5 ]

0 голосов
/ 06 февраля 2019

Похоже, вы ищете утка, набирающую , и это сложнее достичь на языке статической типизации (за некоторыми исключениями, перечисленными на связанной странице Википедии).

Это связано с тем, что для динамического вызова метода требуется знание макета целевого объекта, то есть либо наследование класса, объявляющего метод, либо соответствие протоколу, который требует этот метод.

Запускс Swift 4.2 и введением динамического поиска членов существует другой подход для решения вашей проблемы, однако он также включает в себя некоторую церемонию:

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

В заключение, в текущем SwiftВ версии нет способа, чтобы аргумент и метод были динамическими, необходимо установить общие точки соприкосновения.

0 голосов
/ 06 февраля 2019

Мне понравился этот вопрос, потому что он заставил меня задуматься над мелочью вне коробки.

Я отвечу на него, разделив его на несколько частей.

Первый

Функции класса вызова

Функция класса в основном представляет собой Методы типа , которые могут быть достигнуты с помощью staticслово внутри контекста class.

Принимая это во внимание, вы можете получить простое решение, используя протокол и передавая ссылку на класс (соответствующую этому протоколу) следующим образом:

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

Секунда

Если вам не нужен метод в контексте класса, вы можете рассмотреть возможность использования методов экземпляра.В этом случае решение было бы еще проще, например:

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

Третий

Если вам нужно использовать синтаксис class func, как первоначально делал OP:

class func doSomething ()

Вы НЕ МОЖЕТЕ просто использовать протокол.Поскольку протокол не является классом ... Таким образом, компилятор не допустит его.

Cannot declare class function inside protocol

Но все же возможно, вы можете добиться этого с помощью Селектор с NSObject.perform Метод

как это:

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");

}
0 голосов
/ 06 февраля 2019

Универсальное и протоколно-ориентированное программирование выполнит свою работу:

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 голосов
/ 06 февраля 2019

Я думаю, есть три решения.Я поделился примером ниже.

  1. Используйте "протокол", который имеет требования к функции "doSomething ()".
  2. Создайте функцию, которая получает определение функции в качестве параметра.
  3. Используйте отражение.Вы можете использовать EVReflection , который является хорошим API для отражения.

пример кода:

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)
0 голосов
/ 06 февраля 2019

вы можете достичь этого с помощью протокола

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