سؤال

في روبي ، الكائنات لها طريقة مفيدة تسمى method_missing الذي يسمح للمرء بالتعامل مع مكالمات الطريقة للطرق التي لم يتم تحديدها (بشكل صريح):

استدعى من قبل Ruby عندما يتم إرسال OBJ رسالة لا يمكن التعامل معها. الرمز هو الرمز للطريقة المسمى ، و args هي أي وسيطات تم تمريرها إليها. بشكل افتراضي ، يرفع المترجم المترجم خطأ عندما يتم استدعاء هذه الطريقة. ومع ذلك ، من الممكن تجاوز طريقة توفير سلوك أكثر ديناميكية. المثال أدناه ينشئ فئة رومانية ، والتي تستجيب للطرق مع أسماء تتكون من الأرقام الرومانية ، وإعادة قيم عدد صحيح المقابلة.

class Roman
 def romanToInt(str)
   # ...
 end
 def method_missing(methId)
   str = methId.id2name
   romanToInt(str)
 end
end

r = Roman.new
r.iv      #=> 4
r.xxiii   #=> 23
r.mm      #=> 2000

على سبيل المثال ، يستخدم Ruby on Rails هذا للسماح للمكالمات بطرق مثل find_by_my_column_name.

سؤالي هو ، ما هي اللغات الأخرى التي تدعم ما يعادلها method_missing, ، وكيف يمكنك تنفيذ ما يعادله في الكود الخاص بك؟

هل كانت مفيدة؟

المحلول

بعض حالات استخدام method_missing يمكن تنفيذها في بيثون باستخدام __getattr__ على سبيل المثال

class Roman(object):
  def roman_to_int(self, roman):
    # implementation here

  def __getattr__(self, name):
    return self.roman_to_int(name)

ثم يمكنك أن تفعل:

>>> r = Roman()
>>> r.iv
4

نصائح أخرى

Smalltalk لديه doesNotUnderstand الرسالة ، التي ربما تكون التنفيذ الأصلي لهذه الفكرة ، بالنظر إلى أن SmallTalk هو أحد والدي روبي. يعرض التطبيق الافتراضي نافذة خطأ ، ولكن يمكن تجاوزه لفعل شيء أكثر إثارة للاهتمام.

يمكن زيادة تحميل كائنات PHP مع __call طريقة خاصة.

فمثلا:

<?php
class MethodTest {
    public function __call($name, $arguments) {
        // Note: value of $name is case sensitive.
        echo "Calling object method '$name' "
             . implode(', ', $arguments). "\n";
    }
}

$obj = new MethodTest;
$obj->runTest('in object context');
?>

جافا سكريبت nosuchmethod, ، لكن لسوء الحظ ، يتم دعم هذا فقط بواسطة Firefox/Spidermonkey.

هنا مثال:

wittyProjectName.__noSuchMethod__ = function __noSuchMethod__ (id, args) {
   if (id == 'errorize') {
    wittyProjectName.log("wittyProjectName.errorize has been deprecated.\n" +
                         "Use wittyProjectName.log(message, " +
                         "wittyProjectName.LOGTYPE_ERROR) instead.",
                         this.LOGTYPE_LOG);
    // just act as a wrapper for the newer log method
    args.push(this.LOGTYPE_ERROR);
    this.log.apply(this, args);
  }
}

بيرل لديه AUTOLOAD الذي يعمل على الروتين الفرعي وطرق الفئة/الكائن.

مثال فرعي:

use 5.012;
use warnings;

sub AUTOLOAD {
    my $sub_missing = our $AUTOLOAD;
    $sub_missing =~ s/.*:://;
    uc $sub_missing;
}

say foo();   # => FOO

مثال على طريقة الفئة/الكائن: مثال:

use 5.012;
use warnings;

{
    package Shout;

    sub new { bless {}, shift }

    sub AUTOLOAD {
        my $method_missing = our $AUTOLOAD;
        $method_missing =~ s/.*:://;
        uc $method_missing;
    }
}

say Shout->bar;         # => BAR

my $shout = Shout->new;
say $shout->baz;        # => BAZ

الهدف-C يدعم نفس الشيء ويطلق عليه إعادة التوجيه.

تم إنجاز هذا في لوا عن طريق ضبط __index مفتاح أ metatable.

t = {}
meta = {__index = function(_, idx) return function() print(idx) end end}
setmetatable(t, meta)

t.foo()
t.bar()

سيتم إخراج هذا الرمز:

foo
bar

كنت أبحث عن هذا من قبل ، ووجدت أ قائمة مفيدة (يتم تجاوزه بسرعة هنا) كجزء من مشروع MERD على SourceForge.


 Construct                          Language
-----------                        ----------
 AUTOLOAD                           Perl
 AUTOSCALAR, AUTOMETH, AUTOLOAD...  Perl6
 __getattr__                        Python
 method_missing                     Ruby
 doesNotUnderstand                  Smalltalk
 __noSuchMethod__(17)               CoffeeScript, JavaScript
 unknown                            Tcl
 no-applicable-method               Common Lisp
 doesNotRecognizeSelector           Objective-C
 TryInvokeMember(18)                C#
 match [name, args] { ... }         E
 the predicate fail                 Prolog
 forward                            Io

مع الحواشي:

  • (17) Firefox
  • (18) ج# 4 ، فقط للكائنات "الديناميكية"

في Lisp المشتركة ، no-applicable-method يمكن استخدامها لهذا الغرض ، وفقا ل مواصفات شائعة LISP Hyper:

يتم استدعاء الوظيفة العامة التي لا يمكن تطبيقها عندما يتم استدعاء وظيفة عامة ولا توجد طريقة على هذه الوظيفة العامة قابلة للتطبيق. تشير الطريقة الافتراضية إلى خطأ.

الوظيفة العامة لا يمكن أن تُهدف الوظيفة التي لا يمكن تطبيقها للتطبيق إلى أن يطلق عليها المبرمجون. قد يكتب المبرمجون طرقًا لذلك.

على سبيل المثال:

(defmethod no-applicable-method (gf &rest args)
  ;(error "No applicable method for args:~% ~s~% to ~s" args gf)
  (%error (make-condition 'no-applicable-method :generic-function gf :arguments args) '()
        ;; Go past the anonymous frame to the frame for the caller of the generic function
        (parent-frame (%get-frame-ptr))))

ج# لديه الآن TryInvokemmember, ، للكائنات الديناميكية (الوراثة من DynamicObject)

ActionScript 3.0 لديه ملف Proxy فئة يمكن تمديدها لتوفير هذه الوظيفة.

dynamic class MyProxy extends Proxy {
  flash_proxy override function callProperty(name:*, ...rest):* {
    try {
      // custom code here
    }
    catch (e:Error) {
      // respond to error here
    }
}  

TCL لديه شيء مشابه. في أي وقت تتصل به أي أمر لا يمكن العثور عليه ، الإجراء مجهول وسوف يطلق. على الرغم من أنه ليس شيئًا تستخدمه عادةً ، إلا أنه يمكن أن يكون مفيدًا في بعض الأحيان.

في CFML (Coldfusion ، Railo ، OpenBD) ، onMissingMethod() سيتلقى معالج الأحداث ، المحدد داخل مكون ، مكالمات غير محددة على هذا المكون. الحجج missingMethodName و missingMethodArguments يتم تمريرها تلقائيًا ، مما يتيح التعامل الديناميكي لمكالمة الطريقة المفقودة. هذه هي الآلية التي سهلت إنشاء مخططات Setter/Getter الضمنية قبل أن تبدأ في بناء محركات CFML المختلفة.

ما يعادلها Io يستخدم forward طريقة.

من المستندات:

إذا لم يرد كائن على رسالة ، فسوف يستدعي طريقة "الأمام" إذا كان لديه واحد ....

اليك مثال بسيط:

Shout := Object clone do (
    forward := method (
        method_missing := call message name
        method_missing asUppercase
    )
)

Shout baz println     # => BAZ

/i3az/

بوو لديه IQuackFu - هناك بالفعل ملخص ممتاز على ذلك في How-Can-i-Intercept-A-Method-Call-In-Boo

هنا مثال:

class XmlObject(IQuackFu):
_element as XmlElement 

def constructor(element as XmlElement):
    _element = element 

def QuackInvoke(name as string, args as (object)) as object:
    pass # ignored 

def QuackSet(name as string, parameters as (object), value) as object:
    pass # ignored 

def QuackGet(name as string, parameters as (object)) as object:
    elements = _element.SelectNodes(name)
    if elements is not null:
        return XmlObject(elements[0]) if elements.Count == 1
        return XmlObject(e) for e as XmlElement in elements 

override def ToString():
    return _element.InnerText 
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top