如何从PowerShell中的静态方法动态引用类和静态属性?

ffx8fchx  于 2023-04-21  发布在  Shell
关注(0)|答案(2)|浏览(155)

我有一个名为DatabaseObject的超类。这个类实现了Active Record设计模式,并且将被所有访问数据库的其他继承。
PHP中,我可以这样写:

class DatabaseObject {
    protected static $database;
    ...

    public static function set_database($database) {
        self::$database = $database;
    }

    public static function find_all() {
        $sql = "SELECT * FROM " . static::$table_name;
        return static::find_by_sql($sql);
    }

    protected static function instantiate($record) {
        $object = new static;
        foreach($record as $property => $value) {
            if( property_exists($object, $property) ) {
                $object->$property = $value;
            }
        }
        return $object;
    }
    ...
}

注意,使用关键字self::static::引用类的dynamic方式。在该语言中,我们甚至可以这样做:

$object = new static;

Python中,我们可以像这样创建一个类的示例:

@classmethod
def _instantiate(cls, record):
    # Creates an instance of the subclass.
    obj = cls

TypeScript中,我们可以做类似的事情:

protected static instantiate(record: object): object {
    const obj = new this();
    ...
    return obj;
}

如何在PowerShell中达到同样的效果?
现在,我只能这样做:

static [void]SetDatabase($Database) {
        [DatabaseObject]::Database = $Database
}

**我必须显式地写类名。**如果类应该被继承,它将无法工作。

你能告诉我做这件事的正确方法吗?
谢谢大家。

更新(@圣地亚哥Squarzon的贡献之后):

为了澄清并可能在未来帮助其他人,我将给予更多细节。
这个类有示例属性和方法,但为了保持清晰,我选择不显示它们。
静态方法应该能够设置静态属性和调用(传递参数)其他静态方法。
在静态方法中,将返回类型设置为自定义类型(子类的示例)可能会有问题。但总是可以返回包含子类示例的ArrayList。

kwvwclae

kwvwclae1#

PowerShell中self的等价物是$this,但只能在示例方法上使用,而不能在静态方法上使用。基本上,如果你想从静态方法中 * 动态 * 反射类型成员,你能做的最好的事情就是将类型本身作为参数传递。此外,我不确定从一个只有静态成员的类继承有多大用处,但你可能有一个很好的理由。

class Test {
    static [string] $Prop

    # with an instance method, can use `$this`
    [void] SetProp([string] $Value) {
        $this::Prop = $value
    }

    # with a static method, need to pass the target type
    static [void] SetProp([type] $TargetType, [string] $Value) {
        $TargetType::Prop = $Value
    }
}

[Test]::new().SetProp('foo')
[Test]::Prop # foo
[Test]::SetProp([Test], 'bar')
[Test]::Prop # bar

除此之外,你可能会发现C#比PowerShell更好的类支持,在C#中,你不需要引用类型本身来分配属性的值。例如:

Add-Type '
public static class Test
{
    public static string Prop { get; internal set; }

    public static void SetProp(string value)
    {
        Prop = value;
    }
}
'

[Test]::Prop = 'foo' # Error, ReadOnly property
[Test]::SetProp('foo')
[Test]::Prop # foo

然而,你很快就会发现你不能从静态类继承,这暗示了上面提到的。你可能需要重新考虑你想做什么。最有可能的是应该使用一个示例方法和一个示例属性。
静态类是密封的,因此不能被继承。它们不能从除Object之外的任何类或接口继承。
参见Static Classes and Static Class Members (C# Programming Guide)

u0njafvf

u0njafvf2#

在@圣地亚哥Squarzon的贡献之后,我能够这样写:

static [System.Collections.ArrayList]FindBySql([type]$TargetType, $Sql) {

    $Result = $TargetType::Database.Query($Sql, $false)
    if (-not $Result) {
        $ErrorMessage = "Database query failed."
        PrintErrorMessage -ErrorMessage $ErrorMessage
        throw $ErrorMessage
    }

    # Results into objects
    $ObjectArray = [System.Collections.ArrayList]@()
    $NewInstance = $null

    for ($i = 0; $i -lt $Result.Count; $i++) {
        $NewInstance = $TargetType::Instantiate($TargetType, $Result[$i])[0]
        $ObjectArray.Add( $NewInstance )
    }

    # My alternative to mysqli_result::free() method.
    Clear-Variable -Name "Result"

    return $ObjectArray

}

hidden static [System.Collections.ArrayList]Instantiate([type]$TargetType, $Record) {
    
    $ObjectArray = [System.Collections.ArrayList]@()
    $Object = $TargetType::new($null)

    foreach($Key in $Record.Keys) {
        if (Get-Member -inputobject $Object -name $Key -Membertype Properties) {
            $Object.$Key = $Record[$Key]
        }
    }

    $ObjectArray.Add($Object)
    return $ObjectArray
}

在Main.ps1文件中,可以像这样调用静态方法:

$Sql = "SELECT * FROM admins"
$Result = [Admins]::FindBySql([Admins], $Sql)

Admins是DatabaseObject的子类。

相关问题