PHP中的C#类扩展方法?

cae*_*say 21 php c#

我喜欢在C#中编写扩展方法的方式,然后执行以下操作:

string ourString = "hello";
ourString.MyExtension("another");
Run Code Online (Sandbox Code Playgroud)

甚至

"hello".MyExtention("another");
Run Code Online (Sandbox Code Playgroud)

有没有办法在PHP中有类似的行为?

dec*_*eze 10

如果您将所有字符串重新实现为对象,则可以.

class MyString {
    ...
    function foo () { ... }
}

$str = new MyString('Bar');
$str->foo('baz');
Run Code Online (Sandbox Code Playgroud)

但你真的不想这样做.PHP本身并不是面向对象的语言,字符串只是原始类型而没有方法.

如果'Bar'->foo('baz')不扩展核心引擎(这不是你想要进入的东西,至少不是为了这个目的:),PHP中的语法是不可能实现的.).


也没有任何关于扩展对象的功能,这使得它本身比简单地编写接受基元的新函数更好.换句话说,PHP相当于

"hello".MyExtention("another");
Run Code Online (Sandbox Code Playgroud)

my_extension("hello", "another");
Run Code Online (Sandbox Code Playgroud)

出于所有目的和目的,它具有相同的功能,只是不同的语法.

  • 地狱是的,PHP设计师非常喜欢`my_extension()`方法,这里是[**可爱的结果**](http://www.php.net/manual/en/indexes.functions.php):) (11认同)
  • 祝你好运,链接很多方法。在 C# 中,您可以执行“test”.ToUpper().ToLower.Split().Join().Trim()。现在我什至不需要编写 PHP 代码来实现这一点。 (2认同)
  • “无论出于何种目的,它都具有相同的功能,只是语法不同。” 在这种情况下,语法就是所需的功能。 (2认同)

Rol*_*kas 5

从PHP 5.4开始,有一些特性可以用作扩展方法。

例:

<?php
trait HelloWorld {
    public function sayHelloWorld() {
        echo 'Hello World';
    }
}

class MyHelloWorld {
    use HelloWorld;
    public function sayExclamationMark() {
        echo '!';
    }
}

$o = new MyHelloWorld();
$o->sayHelloWorld();
$o->sayExclamationMark();
?> 
Run Code Online (Sandbox Code Playgroud)

结果:

Hello World!
Run Code Online (Sandbox Code Playgroud)

一旦将特征包含到类中,就可以用一个名称进行调用,例如Extension,您可以添加所需的任何方法并将其定位在其他位置。然后在该示例中,use Extension对于可扩展类,它仅是一次性装饰。

  • 特性与扩展方法不同,因为它们仍需要修改原始类。 (15认同)
  • @AlexanderTrauzzi 所以你观察到它们并不相同,并声称 - 甚至不是替代品。为无人。那是对的吗?你现在想发生什么? (2认同)

小智 5

我在 PHP >= 5.3.0 中有另一个实现,它就像 Northborn Design 解释的装饰器。

我们只需要一个 API 来创建扩展和一个装饰器来应用扩展。

我们必须记住,在 C# 扩展方法上,它们不会破坏扩展对象的封装,也不会修改对象(没有意义,实现方法会更有效)。扩展方法是纯静态的,它们接收对象的实例,如下例所示(C#,来自 MSDN):

public static int WordCount(this String str)
{
    return str.Split(new char[] { ' ', '.', '?' }, 
                     StringSplitOptions.RemoveEmptyEntries).Length;
}
Run Code Online (Sandbox Code Playgroud)

当然,我们在 PHP 中没有 String 对象,但是对于所有其他对象,我们可以为这种魔法创建通用装饰器。

让我们看看我的实现:

API:

<?php

namespace Pattern\Extension;

/**
 * API for extension methods in PHP (like C#).
 */
class Extension
{
    /**
     * Apply extension to an instance.
     *
     * @param object $instance
     * @return \Pattern\Extension\ExtensionWrapper
     */
    public function __invoke($instance)
    {
        return Extension::apply($instance);
    }

    /**
     * Apply extension to an instance.
     *
     * @param object $instance
     * @return \Pattern\Extension\ExtensionWrapper
     */
    public static function apply($instance)
    {
        return new ExtensionWrapper($instance, \get_called_class());
    }

    /**
     * @param mixed $instance
     * @return boolean
     */
    public static function isExtensible($instance)
    {
        return ($instance instanceof Extensible);
    }
}
?>
Run Code Online (Sandbox Code Playgroud)

装饰师:

<?php

namespace Pattern\Extension;

/**
 * Demarcate decorators that resolve the extension.
 */
interface Extensible
{
    /**
     * Verify the instance of the holded object.
     *
     * @param string $className
     * @return bool true if the instance is of the type $className, false otherwise.
     */
    public function holdsInstanceOf($className);

    /**
     * Returns the wrapped object.
     * If the wrapped object is a Extensible the returns the unwrap of it and so on.
     *
     * @return mixed
     */
    public function unwrap();

    /**
     * Magic method for the extension methods.
     *
     * @param string $name
     * @param array $args
     * @return mixed
     */
    public function __call($name, array $args);
}
?>
Run Code Online (Sandbox Code Playgroud)

和通用实现:

<?php

namespace Pattern\Extension;

/**
 * Generic version for the Extensible Interface.
 */
final class ExtensionWrapper implements Extensible
{
    /**
     * @var mixed
     */
    private $that;

    /**
     * @var Extension
     */
    private $extension;

    /**
     * @param object $instance
     * @param string | Extension $extensionClass
     * @throws \InvalidArgumentException
     */
    public function __construct($instance, $extensionClass)
    {
        if (!\is_object($instance)) {
            throw new \InvalidArgumentException('ExtensionWrapper works only with objects.');
        }

        $this->that = $instance;
        $this->extension = $extensionClass;
    }

    /**
     * {@inheritDoc}
     * @see \Pattern\Extension\Extensible::__call()
     */
    public function __call($name, array $args)
    {
        $call = null;
        if (\method_exists($this->extension, '_'.$name)) {
            // this is for abstract default interface implementation
            \array_unshift($args, $this->unwrap());
            $call = array($this->extension, '_'.$name);
        } elseif (\method_exists($this->extension, $name)) {
            // this is for real implementations
            \array_unshift($args, $this->unwrap());
            $call = array($this->extension, $name);
        } else {
            // this is for real call on object
            $call = array($this->that, $name);
        }
        return \call_user_func_array($call, $args);
    }

    /**
     * {@inheritDoc}
     * @see \Pattern\Extension\Extensible::unwrap()
     */
    public function unwrap()
    {
        return (Extension::isExtensible($this->that) ? $this->that->unwrap() : $this->that);
    }

    /**
     * {@inheritDoc}
     * @see \Pattern\Extension\Extensible::holdsInstanceof()
     */
    public function holdsInstanceOf($className)
    {
        return \is_a($this->unwrap(), $className);
    }
}
?>
Run Code Online (Sandbox Code Playgroud)

使用:

假设存在第三方类:

class ThirdPartyHello
{
    public function sayHello()
    {
        return "Hello";
    }
}
Run Code Online (Sandbox Code Playgroud)

创建你的扩展:

use Pattern\Extension\Extension;

class HelloWorldExtension extends Extension
{
    public static function sayHelloWorld(ThirdPartyHello $that)
    {
        return $that->sayHello().' World!';
    }
}
Run Code Online (Sandbox Code Playgroud)

另外:对于界面爱好者,创建一个抽象扩展:

<?php
interface HelloInterfaceExtension
{
    public function sayHelloFromInterface();
}
?>
<?php
use Pattern\Extension\Extension;

abstract class AbstractHelloExtension extends Extension implements HelloInterfaceExtension
{
    public static function _sayHelloFromInterface(ThirdPartyOrLegacyClass $that)
    {
        return $that->sayHello(). ' from Hello Interface';
    }
}
?>
Run Code Online (Sandbox Code Playgroud)

然后使用它:

////////////////////////////
// You can hide this snippet in a Dependency Injection method

$thatClass = new ThirdPartyHello();

/** @var ThirdPartyHello|HelloWorldExtension $extension */
$extension = HelloWorldExtension::apply($thatClass);

//////////////////////////////////////////

$extension->sayHello(); // returns 'Hello'
$extension->sayHelloWorld(); // returns 'Hello World!'

//////////////////////////////////////////
// Abstract extension

$thatClass = new ThirdPartyHello();

/** @var ThirdPartyHello|HelloInterfaceExtension $extension */
$extension = AbstractHelloExtension::apply($instance);

$extension->sayHello(); // returns 'Hello'
$extension->sayHelloFromInterface(); // returns 'Hello from Hello Interface'
Run Code Online (Sandbox Code Playgroud)

优点:

  • 与 PHP 中的 C# 扩展方法非常相似;
  • 不能直接将扩展实例作为扩展对象的实例进行测试,但这很好,因为它更安全,因为我们可以有不扩展该类实例的地方;
  • 作为提高团队敏捷性的框架的意图,您必须减少编写;
  • 扩展的使用似乎是对象的一部分,但它只是被装饰了(也许这对团队快速开发很有趣,但如果涉及遗留,则在未来审查该扩展对象的实现);
  • 您可以直接使用扩展的静态方法来提高性能,但这样做会失去模拟部分代码的能力(高度指示 DI)。

缺点:

  • 扩展必须声明给对象,它不仅仅是像 C# 中的导入一样,您必须“装饰”所需的实例以对其进行扩展。
  • 不能直接测试一个扩展实例作为扩展对象的实例,更多代码使用API​​进行测试;
  • 由于使用魔术方法而导致的性能缺陷(但当需要性能时,我们会更改语言,重新创建核心,使用极简框架,如果需要,则使用汇编器);

这是该 Api 的要点:https : //gist.github.com/tennaito/9ab4331a4b837f836ccdee78ba58dff8