2010-11-22 45 views
17

Làm thế nào để bạn gọi một phương thức tĩnh chung của một lớp tùy chỉnh trong Powershell?Gọi phương thức tĩnh chung trong PowerShell

Với lớp sau đây:

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

Và điều này được biên dịch vào một hội đồng 'Classes.dll' và nạp vào PowerShell như thế này:

Add-Type -Path "Classes.dll" 

gì là cách dễ nhất để gọi MyMethod phương pháp?

Trả lời

10

Bạn có thể gọi các phương thức chung, tham khảo bài đăng Invoking Generic Methods on Non-Generic Classes in PowerShell.

Điều này không đơn giản, bạn cần sử dụng hàm MakeGenericMethod. Nó là khá đơn giản nếu phương pháp không có ghi đè, nó sẽ khó khăn hơn nếu nó.

Chỉ trong trường hợp, mã sao chép dán từ đó:

## 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" 
+2

Xin lỗi nhưng tôi đứng theo tuyên bố của tôi - 'không thể được thực hiện * trực tiếp * trong PowerShell'. :-) Tuyệt vời làm việc xung quanh BTW ... nhưng thực sự, nhóm PowerShell cần phải sửa lỗ này. –

+0

Đồng ý với Keith rằng sẽ rất tuyệt khi có hỗ trợ tích hợp cho điều này, nhưng vì đây là giải pháp (ngay cả khi không trực tiếp) câu trả lời này nhận được dấu tích. –

+0

Mẫu mã dài dòng là không cần thiết để giải quyết OP, MakeGenericMethod là đủ. – JohnC

5

Đây là giới hạn của PowerShell và không thể thực hiện trực tiếp trong PowerShell V1 hoặc V2 AFAIK.

BTW phương pháp chung của bạn không thực sự chung chung. Không được:

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

Nếu bạn sở hữu mã này và muốn sử dụng nó từ PowerShell, tránh phương pháp chung hoặc viết phương pháp C# không chung chung.

+0

Bạn đang đúng về phương pháp .. Tôi đã đơn giản hóa nó cho câu hỏi và có thể đã đi quá xa :-) –

1

cách nhanh chóng, nếu không có mâu thuẫn tên:

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

Cách dễ nhất để gọi MyMethod là, như @Athari nói, sử dụng MakeGenericMethod. Kể từ khi anh ấy không thực sự hiển thị như thế nào để làm điều đó, đây là một mẫu mã làm việc xác nhận:

$obj = New-Object Sample 

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

với sản lượng

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

wow, hữu ích. Cảm ơn bạn – Pisu

2

Tin tốt là PowerShell v3 là tốt hơn nhiều tại ràng buộc với các phương pháp chung (và reifying chúng?) và bạn thường không phải làm bất cứ điều gì đặc biệt nhưng gọi nó như bạn sẽ là một phương pháp bình thường. Tôi không thể chỉ định tất cả các tiêu chí mà bây giờ hoạt động, nhưng trong kinh nghiệm của tôi một số tình huống với các tham số chung vẫn yêu cầu giải pháp ngay cả trong PowerShell v4 (có thể là sự tồn tại hoặc quá tải hoặc tương tự).

Tương tự, đôi khi tôi cũng gặp sự cố khi chuyển một tham số chung cho một phương thức ... ví dụ: chuyển thông số Func<T1, T2, TResult>.

Một công việc xung quanh đó với tôi là đơn giản hơn nhiều so với MakeGenericMethod hoặc cách tiếp cận khác là chỉ cần đặt một C# wrapper lớp nhanh chóng trực tiếp trong kịch bản của tôi, và để cho C# loại bỏ tất cả các bản đồ chung ...

đây là một ví dụ về cách tiếp cận này kết thúc tốt đẹp phương pháp Enumerable.Zip. Trong ví dụ này, lớp C# của tôi không phải là chung chung nhưng điều đó không nói đúng.

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

này tạo ra:

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

tôi chắc chắn rằng có những cách tốt hơn để PowerShell "Zip" hai mảng nhưng bạn sẽ có được ý tưởng. Thách thức thực sự mà tôi né tránh ở đây là có một tham số thứ 3 (trong lớp C#) cứng đến Zip vì vậy tôi không phải tìm ra cách vượt qua trong đó Func<T1, T2, TResult> (Có thể có cách PowerShell để thực hiện điều đó ?).

Các vấn đề liên quan