2016-03-14 173 views
5

我目前正在嘗試使用PowerShell 3.0中引入的AST功能來修改ScriptBlock。我的要求是,ScriptBlock的參數塊中的所有參數都獲得[Parameter(Mandatory)]屬性。PowerShell AST修改和範圍

基本上代碼應該修改此:

Param([string]$x) 

Write-Host $x 

這樣:

Param([Parameter(Mandatory)][string]$x) 

Write-Host $x 

但是,我補充說,新的屬性的時候,因爲它期望的IScriptExtent遇到了一個問題,我不當然,我應該如何創建一個新的IScriptExtent

如何創建新的腳本範圍?我可以使用什麼值作爲職位?我是否必須改變以下所有程度的位置?

我試圖重新使用我修改的每個參數的範圍,但不幸的是,這似乎並沒有產生它應該的結果(例如,當我在修改的ScriptBlock上調用ToString時,我沒有看到任何更改)。

我到目前爲止的實現是基於ICustomAstVisitor找到here

最重要的方法是這樣的:

public object VisitParameter(ParameterAst parameterAst) 
{ 
    var newName = VisitElement(parameterAst.Name); 

    var extent = // What to do here? 

    var mandatoryArg = new AttributeAst(extent, new ReflectionTypeName(typeof (ParameterAttribute)), 
     new ExpressionAst[0], 
     new[] {new NamedAttributeArgumentAst(extent, "Mandatory", new ConstantExpressionAst(extent, true), true)}); 

    var newAttributes = new[] {mandatoryArg}.Concat(VisitElements(parameterAst.Attributes)); 
    var newDefaultValue = VisitElement(parameterAst.DefaultValue); 
     return new ParameterAst(parameterAst.Extent, newName, newAttributes, newDefaultValue); 
} 

回答

3

名稱與I開頭通常是接口。它們不是您創建實例的類,它們是指定某個特定類實現某個已知功能集的排序合約。例如,[hashtable]實施IEnumerable。這意味着任何知道如何使用IEnumerable接口並在該類上運行的任何東西;你可以創建你自己的實現接口的類,並且代碼永遠不可能知道你的類或者它能夠以IEnumerable定義的方式與它進行交互(在這種情況下是迭代它的方式)。

所以,當一個函數聲明一個帶有接口類型的參數時,它並不尋找任何一個特定的類,它正在尋找任何實現該接口的類。

接下來的一步是找到哪個類型實現該接口。下面是一些PowerShell代碼我用來尋找那些:

[System.AppDomain]::CurrentDomain.GetAssemblies().GetTypes() | Where-Object { 
    [System.Management.Automation.Language.IScriptExtent].IsAssignableFrom($_) 
} 

由此,我們可以看到以下內容:

IsPublic IsSerial Name          BaseType              
-------- -------- ----          --------              
True  False IScriptExtent                       
False False InternalScriptExtent      System.Object            
False False EmptyScriptExtent      System.Object            
True  False ScriptExtent        System.Object            

首先上市的是接口本身。其他三個,其中兩個不公開,所以只剩下ScriptExtent

您可以使用New-Object創建其中的一個,但您需要提供開始和結束位置作爲[ScriptPosition]對象。沒有看到更多的代碼,我不完全確定這些應該是什麼。

+0

我知道的接口是什麼,我的問題圍繞在我身邊不太能夠弄清楚創造新的代碼時,範圍在PowerShell中的工作方式(有很多人修改代碼和重用程度的例子,但我找不到任何人創建新代碼的例子)。 – chrischu

+1

@chrischu從你的問題來看,你不太清楚你是否熟悉界面,因爲你問了如何創建一個新的'IScriptExtent',所以我覺得最好在安全的方面犯錯並解釋,特別是因爲它可能對你有幫助其他訪問者發現你的問題,但不知道接口是什麼。你也可以考慮在你的問題中包括你到目前爲止所嘗試的內容。 – briantist

3

腳本程度主要用於錯誤報告,但也可用於調試(例如,設定一個行斷點)

通常,用於合成的腳本的選項(如你的例子)是:

  • 重用現有的AST,想必不久/與您要添加
  • 使用空AST(基本上創建ScriptExtent和ScriptPosition的情況下,沒有文件,空行)
  • 創建一個合成的AST在某種程度上有助於調試,也許有一些特殊的內容

在你的例子中,任何上述都適用。第二種選擇是最簡單的。第三個選項只是第二個選項的變體,但您可以將內容設置爲有用的內容,例如

<#Generated: [Parameter(Mandatory)] #> 
+0

有趣。雖然我有一個問題,那就是在修改後的AST上調用ToString()會返回舊的代碼,我的想法是,這是因爲舊的擴展。是否有另一種好的方法可以將AST轉換回源代碼,而不依賴於範圍是否正確? – chrischu

+0

不那麼容易 - 漂亮的打印機是基於Ast實現的完全合理的東西,但我不知道其中的一個,除了我很久以前寫的一些示例代碼,其實並沒有做得很好格式。 –

+2

那麼「漂亮的打印機」是整個問題:我甚至不需要代碼變得漂亮,我只需要在修改AST之後獲取代碼,並且這在沒有提供「正確」範圍的情況下似乎不起作用。 以某種方式,它甚至似乎更容易使用字符串修改來修改代碼,而不是涉及AST,這將是相當可恥的。 – chrischu