programing

PowerShell에서 일반 정적 메서드 호출

padding 2023. 10. 10. 20:08
반응형

PowerShell에서 일반 정적 메서드 호출

파워셸에서 사용자 정의 클래스의 일반 정적 메서드를 어떻게 부르나요?

다음과 같은 클래스가 주어집니다.

public class Sample
{
    public static string MyMethod<T>( string anArgument )
    {
        return string.Format( "Generic type is {0} with argument {1}", typeof(T), anArgument );
    }
}

그리고 이것은 'Classes.dll' 어셈블리로 컴파일되어 다음과 같이 PowerShell에 로드됩니다.

Add-Type -Path "Classes.dll"

MyMethod 메서드를 호출하는 가장 쉬운 방법은 무엇입니까?

MyMethod를 호출하는 가장 쉬운 방법은 @Athari의 말처럼 MakeGenericMethod를 사용하는 것입니다.실제로 그 방법을 보여주지 않기 때문에, 검증된 작동 코드 샘플은 다음과 같습니다.

$obj = New-Object Sample

$obj.GetType().GetMethod("MyMethod").MakeGenericMethod([String]).Invoke($obj, "Test Message")
$obj.GetType().GetMethod("MyMethod").MakeGenericMethod([Double]).Invoke($obj, "Test Message")

산출량이 있는

Generic type is System.String with argument Test Message
Generic type is System.Double with argument Test Message

일반 메서드를 호출할 수 있습니다. PowerShell의 Non-Generic Classs에 대한 일반 메서드 호출 게시글을 참조하십시오.

이것은 간단하지 않습니다. 사용해야 합니다.MakeGenericMethod기능.방법에 오버라이드가 없으면 매우 간단하지만, 오버라이드가 있으면 더 어려워집니다.

만일의 경우를 대비해, 거기서 코드를 복사해서 붙여넣기:

## Invoke-GenericMethod.ps1 
## Invoke a generic method on a non-generic type: 
## 
## Usage: 
## 
##   ## Load the DLL that contains our class
##   [Reflection.Assembly]::LoadFile("c:\temp\GenericClass.dll")
##
##   ## Invoke a generic method on a non-generic instance
##   $nonGenericClass = New-Object NonGenericClass
##   Invoke-GenericMethod $nonGenericClass GenericMethod String "How are you?"
##
##   ## Including one with multiple arguments
##   Invoke-GenericMethod $nonGenericClass GenericMethod String ("How are you?",5)
##
##   ## Ivoke a generic static method on a type
##   Invoke-GenericMethod ([NonGenericClass]) GenericStaticMethod String "How are you?"
## 

param(
    $instance = $(throw "Please provide an instance on which to invoke the generic method"),
    [string] $methodName = $(throw "Please provide a method name to invoke"),
    [string[]] $typeParameters = $(throw "Please specify the type parameters"),
    [object[]] $methodParameters = $(throw "Please specify the method parameters")
    ) 

## Determine if the types in $set1 match the types in $set2, replacing generic
## parameters in $set1 with the types in $genericTypes
function ParameterTypesMatch([type[]] $set1, [type[]] $set2, [type[]] $genericTypes)
{
    $typeReplacementIndex = 0
    $currentTypeIndex = 0

    ## Exit if the set lengths are different
    if($set1.Count -ne $set2.Count)
    {
        return $false
    }

    ## Go through each of the types in the first set
    foreach($type in $set1)
    {
        ## If it is a generic parameter, then replace it with a type from
        ## the $genericTypes list
        if($type.IsGenericParameter)
        {
            $type = $genericTypes[$typeReplacementIndex]
            $typeReplacementIndex++
        }

        ## Check that the current type (i.e.: the original type, or replacement
        ## generic type) matches the type from $set2
        if($type -ne $set2[$currentTypeIndex])
        {
            return $false
        }
        $currentTypeIndex++
    }

    return $true
}

## Convert the type parameters into actual types
[type[]] $typedParameters = $typeParameters

## Determine the type that we will call the generic method on. Initially, assume
## that it is actually a type itself.
$type = $instance

## If it is not, then it is a real object, and we can call its GetType() method
if($instance -isnot "Type")
{
    $type = $instance.GetType()
}

## Search for the method that:
##    - has the same name
##    - is public
##    - is a generic method
##    - has the same parameter types
foreach($method in $type.GetMethods())
{
    # Write-Host $method.Name
    if(($method.Name -eq $methodName) -and
    ($method.IsPublic) -and
    ($method.IsGenericMethod))
    {
        $parameterTypes = @($method.GetParameters() | % { $_.ParameterType })
        $methodParameterTypes = @($methodParameters | % { $_.GetType() })
        if(ParameterTypesMatch $parameterTypes $methodParameterTypes $typedParameters)
        {
            ## Create a closed representation of it
            $newMethod = $method.MakeGenericMethod($typedParameters)

            ## Invoke the method
            $newMethod.Invoke($instance, $methodParameters)

            return
        }
    }
}

## Return an error if we couldn't find that method
throw "Could not find method $methodName"

이는 PowerShell의 제한 사항이며 PowerShell V1 또는 V2 AFAIK에서는 직접 수행할 수 없습니다.

그건 그렇고 당신의 일반적인 방법은 사실 일반적이지 않습니다.그래야 하지 않을까요?

public static string MyMethod<T>(T anArgument)
{ 
   return string.Format( "Generic type is {0} with argument {1}", 
                         typeof(T), anArgument.ToString()); 
} 

이 코드를 소유하고 있고 PowerShell에서 사용하려면 일반 메서드를 사용하지 않거나 비일반적인 C# 래퍼 메서드를 작성합니다.

좋은 소식은 PowerShell v3가 일반 메소드에 바인딩(및 리파이닝?)하는 데 훨씬 더 뛰어나다는 것입니다. 또한 일반 메소드처럼 특별한 작업을 수행할 필요가 없는 경우도 많습니다.현재 작동하는 기준을 모두 지정할 수는 없지만, 제 경험상 일반 매개 변수를 사용하는 특정 상황은 PowerShell v4에서도 여전히 해결 방법이 필요합니다(아마도 존재 또는 과부하 또는 유사한 것일 수도 있습니다).

마찬가지로 일반 파라미터를 메소드로 전달하는 데도 어려움을 겪기도 합니다.예를 들어 a를 통과하는.Func<T1, T2, TResult>매개 변수.

MakeGenericMethod나 다른 접근 방식보다 훨씬 간단한 한 가지 해결책은 간단한 C# 래퍼 클래스를 스크립트에 직접 넣고 C#가 모든 일반 매핑을 분류하도록 하는 것입니다.

여기 이 접근법의 예가 있습니다.Enumerable.Zip방법.이 예제에서 제 c# 클래스는 일반적이지는 않지만 엄밀하게 말하면 필요하지는 않습니다.

Add-Type @'
using System.Linq;
public class Zipper
{
    public static object[] Zip(object[] first, object[] second)
    {
        return first.Zip(second, (f,s) => new { f , s}).ToArray();
    }
}
'@
$a = 1..4;
[string[]]$b = "a","b","c","d";
[Zipper]::Zip($a, $b);

이를 통해 생산되는 것은 다음과 같습니다.

 f s
 - -
 1 a
 2 b
 3 c
 4 d

두 개의 어레이를 "ZIP"할 수 있는 더 나은 PowerShell 방법이 있을 것이라고 확신하지만, 이 방법을 이해할 수 있습니다.여기서 제가 피한 진짜 도전은 (C# 클래스에서) 하드 코딩된 세 번째 매개 변수를 사용하는 것이었습니다.Zip 저는 그요를 알 가 없었습니다.Func<T1, T2, TResult>(아마도 PowerShell을 사용할 수 있는 방법이 있을까요?)

이름 충돌이 없는 경우 빠른 방법:

[Sample]::"MyMethod"("arg")

언급URL : https://stackoverflow.com/questions/4241985/calling-generic-static-method-in-powershell

반응형