Как я могу назвать беспараметрический общий метод из Powershell v3?

Например, у меня есть объект .NET $m со следующими перегрузками метода:

PS C:\Users\Me> $m.GetBody

OverloadDefinitions
-------------------    
T GetBody[T]() 
T GetBody[T](System.Runtime.Serialization.XmlObjectSerializer serializer)  

Если я пытаюсь вызвать метод без параметров, я получаю:

PS C:\Users\Me> $m.GetBody()
Cannot find an overload for "GetBody" and the argument count: "0".
At line:1 char:1
+ $m.GetBody()
+ ~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

Я понимаю, что PowerShell v3.0 должен работать более легко с generics. Очевидно, мне нужно как-то сказать, какой тип я хочу вернуть, но я не могу понять синтаксис.

Ответ 1

Похоже, вы пытаетесь вызвать общий метод.

В powershell это можно сделать:

$nonGenericClass = New-Object NonGenericClass
$method = [NonGenericClass].GetMethod("SimpleGenericMethod")
$gMethod = $method.MakeGenericMethod([string]) 
# replace [string] with the type you want to use for T. 
$gMethod.Invoke($nonGenericClass, "Welcome!")

Смотрите этот замечательный пост в блоге для получения дополнительной информации и дополнительных примеров.

В вашем примере вы можете попробовать:

$Source = @" 
public class TestClass
{
    public T Test<T>()
    {
        return default(T);
    }
    public int X;
}
"@ 

Add-Type -TypeDefinition $Source -Language CSharp 
$obj = New-Object TestClass

$Type  = $obj.GetType();
$m =  $Type.GetMethod("Test")
$g = new-object system.Guid
$gType = $g.GetType()
$gm = $m.MakeGenericMethod($gType)
$out = $gm.Invoke( $obj, $null)
#$out will be the default GUID (all zeros)

Это можно упростить, выполнив:

$Type.GetMethod("Test").MakeGenericMethod($gType).Invoke( $obj, $null)

Это тестирование в powershell 2 и powershell 3.

Если бы у вас был более подробный пример того, как вы столкнулись с этим общим методом, я мог бы дать более подробную информацию. Я еще не видел, чтобы какие-либо командлеты Microsoft возвращали все, что давало вам общие методы. Единственный раз, когда это происходит, - это когда используются пользовательские объекты или методы из С# или vb.net.

Чтобы использовать это без каких-либо параметров, вы можете использовать Invoke только с первым параметром.   $ GMethod.Invoke($ nonGenericClass)

Ответ 2

Вызов универсального метода для объекта экземпляр:

$instance.GetType().GetMethod('MethodName').MakeGenericMethod([TargetType]).Invoke($instance, $parameters)

Вызов универсального метода static (см. также Вызов универсального статического метода в PowerShell):

[ClassType].GetMethod('MethodName').MakeGenericMethod([TargetType]).Invoke($null, $parameters)

Обратите внимание, что вы столкнетесь с AmbiguousMatchException, когда есть также не общий вариант метода (см. Как отличить общие и не общие подписи с использованием GetMethod in. NET?). Используйте GetMethods(), затем:

([ClassType].GetMethods() | where {$_.Name -eq "MethodName" -and $_.IsGenericMethod})[0].MakeGenericMethod([TargetType]).Invoke($null, $parameters)

(Помните, что может быть более одного метода, который соответствует указанному выше фильтру, поэтому не забудьте настроить его, чтобы найти тот, который вам нужен.)

Подсказка: вы можете писать сложные типовые литералы типа (см. Общий тип Generic Type в Powershell):

[System.Collections.Generic.Dictionary[int,string[]]]

Ответ 3

Чтобы вызвать (без параметров) общий метод с перегрузками из Powershell v3, как показано в примере OP, используйте сценарий Invoke-GenericMethod.ps1 из ссылки, предоставленной @Chad Carisch, " Вызов общих методов для не общих классов в PowerShell".

Он должен выглядеть примерно так:

Invoke-GenericMethod $m GetBody T @()

Это проверенный пример рабочего кода, который я использую:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Practices.ServiceLocation") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Practices.SharePoint.Common") | Out-Null

$serviceLocator = [Microsoft.Practices.SharePoint.Common.ServiceLocation.SharePointServiceLocator]::GetCurrent()

# Want the PowerShell equivalent of the following C#
# config = serviceLocator.GetInstance<IConfigManager>();

# Cannot find an overload for "GetInstance" and the argument count: "0".
#$config = $serviceLocator.GetInstance()

# Exception calling "GetMethod" with "1" argument(s): "Ambiguous match found."
#$config = $serviceLocator.GetType().GetMethod("GetInstance").MakeGenericMethod([IConfigManager]).Invoke($serviceLocator)

# Correct - using Invoke-GenericMethod
$config = C:\Projects\SPG2013\Main\Scripts\Invoke-GenericMethod $serviceLocator GetInstance Microsoft.Practices.SharePoint.Common.Configuration.IConfigManager @()

$config.CanAccessFarmConfig

Вот альтернативный скрипт, который я еще не пробовал, но более поздний и активно поддерживается, вызывать общие методы из PowerShell.

Ответ 4

полезный ответ marsze содержит большую общую информацию о вызове общих методов, но позвольте мне обратиться к аспекту вызова параметра,

Как намекнул в вопросе:

  • в PSv3+ PowerShell может вывести тип из значений параметров (аргументов), переданных в общий метод,
  • который по определению не может работать с универсальным методом с параметром, потому что нечего делать с типом.

Начиная с Windows PowerShell v5.1/PowerShell Core v6.1.0, PowerShell не имеет синтаксиса, который позволит вам явно указывать тип в этом сценарии.

Тем не менее, есть предложение GitHub усилить синтаксис в PowerShell Core, который в принципе был освещен зеленым светом, но ожидает реализации сообщества.

На данный момент необходимо использовать отражение:

# Invoke $m.GetBody[T]() with [T] instantiated with type [decimal]
$m.GetType().GetMethod('GetBody', [type[]] @()).
    MakeGenericMethod([decimal]).
      Invoke($m, @())
  • .GetMethod('GetBody', [type[]] @()) недвусмысленно обнаруживает перегрузку без параметра .GetBody() из-за передачи в пустом массиве типов параметров.

  • .MakeGenericMethod([decimal]) создает метод с типом примера [decimal].

  • .Invoke($m, @()) затем вызывает метод-экземпляр типа на входном объекте ($m) без аргументов (@(), пустой массив).