Created
November 17, 2011 09:48
-
-
Save FelicePollano/1372806 to your computer and use it in GitHub Desktop.
Dynamic Linq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html xmlns:v="urn:schemas-microsoft-com:vml" | |
xmlns:o="urn:schemas-microsoft-com:office:office" | |
xmlns:w="urn:schemas-microsoft-com:office:word" | |
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" | |
xmlns="http://www.w3.org/TR/REC-html40"> | |
<head> | |
<meta http-equiv=Content-Type content="text/html; charset=windows-1252"> | |
<meta name=ProgId content=Word.Document> | |
<meta name=Generator content="Microsoft Word 12"> | |
<meta name=Originator content="Microsoft Word 12"> | |
<link rel=File-List href="Dynamic%20Expressions_files/filelist.xml"> | |
<title>Dynamic Expression API</title> | |
<link rel=themeData href="Dynamic%20Expressions_files/themedata.thmx"> | |
<link rel=colorSchemeMapping | |
href="Dynamic%20Expressions_files/colorschememapping.xml"> | |
<!--[if gte mso 9]><xml> | |
<w:WordDocument> | |
<w:SpellingState>Clean</w:SpellingState> | |
<w:GrammarState>Clean</w:GrammarState> | |
<w:TrackMoves>false</w:TrackMoves> | |
<w:TrackFormatting/> | |
<w:ValidateAgainstSchemas/> | |
<w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> | |
<w:IgnoreMixedContent>false</w:IgnoreMixedContent> | |
<w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> | |
<w:DoNotPromoteQF/> | |
<w:LidThemeOther>EN-US</w:LidThemeOther> | |
<w:LidThemeAsian>X-NONE</w:LidThemeAsian> | |
<w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> | |
<w:Compatibility> | |
<w:BreakWrappedTables/> | |
<w:SnapToGridInCell/> | |
<w:WrapTextWithPunct/> | |
<w:UseAsianBreakRules/> | |
<w:DontGrowAutofit/> | |
<w:SplitPgBreakAndParaMark/> | |
<w:DontVertAlignCellWithSp/> | |
<w:DontBreakConstrainedForcedTables/> | |
<w:DontVertAlignInTxbx/> | |
<w:Word11KerningPairs/> | |
<w:CachedColBalance/> | |
</w:Compatibility> | |
<w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> | |
<m:mathPr> | |
<m:mathFont m:val="Cambria Math"/> | |
<m:brkBin m:val="before"/> | |
<m:brkBinSub m:val="--"/> | |
<m:smallFrac m:val="off"/> | |
<m:dispDef/> | |
<m:lMargin m:val="0"/> | |
<m:rMargin m:val="0"/> | |
<m:defJc m:val="centerGroup"/> | |
<m:wrapIndent m:val="1440"/> | |
<m:intLim m:val="subSup"/> | |
<m:naryLim m:val="undOvr"/> | |
</m:mathPr></w:WordDocument> | |
</xml><![endif]--><!--[if gte mso 9]><xml> | |
<w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="true" | |
DefSemiHidden="true" DefQFormat="false" DefPriority="99" | |
LatentStyleCount="267"> | |
<w:LsdException Locked="false" Priority="0" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Normal"/> | |
<w:LsdException Locked="false" Priority="9" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="heading 1"/> | |
<w:LsdException Locked="false" Priority="9" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="heading 2"/> | |
<w:LsdException Locked="false" Priority="9" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="heading 3"/> | |
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 4"/> | |
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 5"/> | |
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 6"/> | |
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 7"/> | |
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 8"/> | |
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 9"/> | |
<w:LsdException Locked="false" Priority="39" Name="toc 1"/> | |
<w:LsdException Locked="false" Priority="39" Name="toc 2"/> | |
<w:LsdException Locked="false" Priority="39" Name="toc 3"/> | |
<w:LsdException Locked="false" Priority="39" Name="toc 4"/> | |
<w:LsdException Locked="false" Priority="39" Name="toc 5"/> | |
<w:LsdException Locked="false" Priority="39" Name="toc 6"/> | |
<w:LsdException Locked="false" Priority="39" Name="toc 7"/> | |
<w:LsdException Locked="false" Priority="39" Name="toc 8"/> | |
<w:LsdException Locked="false" Priority="39" Name="toc 9"/> | |
<w:LsdException Locked="false" Priority="35" QFormat="true" Name="caption"/> | |
<w:LsdException Locked="false" Priority="10" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Title"/> | |
<w:LsdException Locked="false" Priority="1" Name="Default Paragraph Font"/> | |
<w:LsdException Locked="false" Priority="11" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Subtitle"/> | |
<w:LsdException Locked="false" Priority="22" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Strong"/> | |
<w:LsdException Locked="false" Priority="20" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Emphasis"/> | |
<w:LsdException Locked="false" Priority="59" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Table Grid"/> | |
<w:LsdException Locked="false" UnhideWhenUsed="false" Name="Placeholder Text"/> | |
<w:LsdException Locked="false" Priority="1" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="No Spacing"/> | |
<w:LsdException Locked="false" Priority="60" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Shading"/> | |
<w:LsdException Locked="false" Priority="61" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light List"/> | |
<w:LsdException Locked="false" Priority="62" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Grid"/> | |
<w:LsdException Locked="false" Priority="63" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 1"/> | |
<w:LsdException Locked="false" Priority="64" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 2"/> | |
<w:LsdException Locked="false" Priority="65" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 1"/> | |
<w:LsdException Locked="false" Priority="66" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 2"/> | |
<w:LsdException Locked="false" Priority="67" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 1"/> | |
<w:LsdException Locked="false" Priority="68" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 2"/> | |
<w:LsdException Locked="false" Priority="69" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 3"/> | |
<w:LsdException Locked="false" Priority="70" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Dark List"/> | |
<w:LsdException Locked="false" Priority="71" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Shading"/> | |
<w:LsdException Locked="false" Priority="72" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful List"/> | |
<w:LsdException Locked="false" Priority="73" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Grid"/> | |
<w:LsdException Locked="false" Priority="60" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Shading Accent 1"/> | |
<w:LsdException Locked="false" Priority="61" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light List Accent 1"/> | |
<w:LsdException Locked="false" Priority="62" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Grid Accent 1"/> | |
<w:LsdException Locked="false" Priority="63" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 1"/> | |
<w:LsdException Locked="false" Priority="64" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 1"/> | |
<w:LsdException Locked="false" Priority="65" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 1 Accent 1"/> | |
<w:LsdException Locked="false" UnhideWhenUsed="false" Name="Revision"/> | |
<w:LsdException Locked="false" Priority="34" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="List Paragraph"/> | |
<w:LsdException Locked="false" Priority="29" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Quote"/> | |
<w:LsdException Locked="false" Priority="30" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Intense Quote"/> | |
<w:LsdException Locked="false" Priority="66" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 2 Accent 1"/> | |
<w:LsdException Locked="false" Priority="67" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 1"/> | |
<w:LsdException Locked="false" Priority="68" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 1"/> | |
<w:LsdException Locked="false" Priority="69" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 1"/> | |
<w:LsdException Locked="false" Priority="70" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Dark List Accent 1"/> | |
<w:LsdException Locked="false" Priority="71" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Shading Accent 1"/> | |
<w:LsdException Locked="false" Priority="72" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful List Accent 1"/> | |
<w:LsdException Locked="false" Priority="73" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Grid Accent 1"/> | |
<w:LsdException Locked="false" Priority="60" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Shading Accent 2"/> | |
<w:LsdException Locked="false" Priority="61" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light List Accent 2"/> | |
<w:LsdException Locked="false" Priority="62" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Grid Accent 2"/> | |
<w:LsdException Locked="false" Priority="63" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 2"/> | |
<w:LsdException Locked="false" Priority="64" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 2"/> | |
<w:LsdException Locked="false" Priority="65" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 1 Accent 2"/> | |
<w:LsdException Locked="false" Priority="66" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 2 Accent 2"/> | |
<w:LsdException Locked="false" Priority="67" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 2"/> | |
<w:LsdException Locked="false" Priority="68" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 2"/> | |
<w:LsdException Locked="false" Priority="69" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 2"/> | |
<w:LsdException Locked="false" Priority="70" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Dark List Accent 2"/> | |
<w:LsdException Locked="false" Priority="71" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Shading Accent 2"/> | |
<w:LsdException Locked="false" Priority="72" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful List Accent 2"/> | |
<w:LsdException Locked="false" Priority="73" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Grid Accent 2"/> | |
<w:LsdException Locked="false" Priority="60" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Shading Accent 3"/> | |
<w:LsdException Locked="false" Priority="61" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light List Accent 3"/> | |
<w:LsdException Locked="false" Priority="62" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Grid Accent 3"/> | |
<w:LsdException Locked="false" Priority="63" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 3"/> | |
<w:LsdException Locked="false" Priority="64" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 3"/> | |
<w:LsdException Locked="false" Priority="65" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 1 Accent 3"/> | |
<w:LsdException Locked="false" Priority="66" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 2 Accent 3"/> | |
<w:LsdException Locked="false" Priority="67" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 3"/> | |
<w:LsdException Locked="false" Priority="68" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 3"/> | |
<w:LsdException Locked="false" Priority="69" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 3"/> | |
<w:LsdException Locked="false" Priority="70" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Dark List Accent 3"/> | |
<w:LsdException Locked="false" Priority="71" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Shading Accent 3"/> | |
<w:LsdException Locked="false" Priority="72" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful List Accent 3"/> | |
<w:LsdException Locked="false" Priority="73" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Grid Accent 3"/> | |
<w:LsdException Locked="false" Priority="60" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Shading Accent 4"/> | |
<w:LsdException Locked="false" Priority="61" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light List Accent 4"/> | |
<w:LsdException Locked="false" Priority="62" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Grid Accent 4"/> | |
<w:LsdException Locked="false" Priority="63" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 4"/> | |
<w:LsdException Locked="false" Priority="64" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 4"/> | |
<w:LsdException Locked="false" Priority="65" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 1 Accent 4"/> | |
<w:LsdException Locked="false" Priority="66" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 2 Accent 4"/> | |
<w:LsdException Locked="false" Priority="67" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 4"/> | |
<w:LsdException Locked="false" Priority="68" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 4"/> | |
<w:LsdException Locked="false" Priority="69" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 4"/> | |
<w:LsdException Locked="false" Priority="70" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Dark List Accent 4"/> | |
<w:LsdException Locked="false" Priority="71" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Shading Accent 4"/> | |
<w:LsdException Locked="false" Priority="72" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful List Accent 4"/> | |
<w:LsdException Locked="false" Priority="73" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Grid Accent 4"/> | |
<w:LsdException Locked="false" Priority="60" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Shading Accent 5"/> | |
<w:LsdException Locked="false" Priority="61" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light List Accent 5"/> | |
<w:LsdException Locked="false" Priority="62" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Grid Accent 5"/> | |
<w:LsdException Locked="false" Priority="63" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 5"/> | |
<w:LsdException Locked="false" Priority="64" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 5"/> | |
<w:LsdException Locked="false" Priority="65" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 1 Accent 5"/> | |
<w:LsdException Locked="false" Priority="66" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 2 Accent 5"/> | |
<w:LsdException Locked="false" Priority="67" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 5"/> | |
<w:LsdException Locked="false" Priority="68" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 5"/> | |
<w:LsdException Locked="false" Priority="69" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 5"/> | |
<w:LsdException Locked="false" Priority="70" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Dark List Accent 5"/> | |
<w:LsdException Locked="false" Priority="71" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Shading Accent 5"/> | |
<w:LsdException Locked="false" Priority="72" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful List Accent 5"/> | |
<w:LsdException Locked="false" Priority="73" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Grid Accent 5"/> | |
<w:LsdException Locked="false" Priority="60" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Shading Accent 6"/> | |
<w:LsdException Locked="false" Priority="61" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light List Accent 6"/> | |
<w:LsdException Locked="false" Priority="62" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Light Grid Accent 6"/> | |
<w:LsdException Locked="false" Priority="63" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 6"/> | |
<w:LsdException Locked="false" Priority="64" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 6"/> | |
<w:LsdException Locked="false" Priority="65" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 1 Accent 6"/> | |
<w:LsdException Locked="false" Priority="66" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium List 2 Accent 6"/> | |
<w:LsdException Locked="false" Priority="67" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 6"/> | |
<w:LsdException Locked="false" Priority="68" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 6"/> | |
<w:LsdException Locked="false" Priority="69" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 6"/> | |
<w:LsdException Locked="false" Priority="70" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Dark List Accent 6"/> | |
<w:LsdException Locked="false" Priority="71" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Shading Accent 6"/> | |
<w:LsdException Locked="false" Priority="72" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful List Accent 6"/> | |
<w:LsdException Locked="false" Priority="73" SemiHidden="false" | |
UnhideWhenUsed="false" Name="Colorful Grid Accent 6"/> | |
<w:LsdException Locked="false" Priority="19" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Subtle Emphasis"/> | |
<w:LsdException Locked="false" Priority="21" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Intense Emphasis"/> | |
<w:LsdException Locked="false" Priority="31" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Subtle Reference"/> | |
<w:LsdException Locked="false" Priority="32" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Intense Reference"/> | |
<w:LsdException Locked="false" Priority="33" SemiHidden="false" | |
UnhideWhenUsed="false" QFormat="true" Name="Book Title"/> | |
<w:LsdException Locked="false" Priority="37" Name="Bibliography"/> | |
<w:LsdException Locked="false" Priority="39" QFormat="true" Name="TOC Heading"/> | |
</w:LatentStyles> | |
</xml><![endif]--> | |
<style> | |
<!-- | |
/* Font Definitions */ | |
@font-face | |
{font-family:"Cambria Math"; | |
panose-1:2 4 5 3 5 4 6 3 2 4; | |
mso-font-charset:1; | |
mso-generic-font-family:roman; | |
mso-font-format:other; | |
mso-font-pitch:variable; | |
mso-font-signature:0 0 0 0 0 0;} | |
@font-face | |
{font-family:Cambria; | |
panose-1:2 4 5 3 5 4 6 3 2 4; | |
mso-font-charset:0; | |
mso-generic-font-family:roman; | |
mso-font-pitch:variable; | |
mso-font-signature:-1610611985 1073741899 0 0 159 0;} | |
@font-face | |
{font-family:Calibri; | |
panose-1:2 15 5 2 2 2 4 3 2 4; | |
mso-font-charset:0; | |
mso-generic-font-family:swiss; | |
mso-font-pitch:variable; | |
mso-font-signature:-1610611985 1073750139 0 0 159 0;} | |
@font-face | |
{font-family:Tahoma; | |
panose-1:2 11 6 4 3 5 4 4 2 4; | |
mso-font-charset:0; | |
mso-generic-font-family:swiss; | |
mso-font-pitch:variable; | |
mso-font-signature:-520082689 -1073717157 41 0 66047 0;} | |
@font-face | |
{font-family:Consolas; | |
panose-1:2 11 6 9 2 2 4 3 2 4; | |
mso-font-charset:0; | |
mso-generic-font-family:modern; | |
mso-font-pitch:fixed; | |
mso-font-signature:-1610611985 1073750091 0 0 159 0;} | |
/* Style Definitions */ | |
p.MsoNormal, li.MsoNormal, div.MsoNormal | |
{mso-style-unhide:no; | |
mso-style-qformat:yes; | |
mso-style-parent:""; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:10.0pt; | |
margin-left:0in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
h1 | |
{mso-style-priority:9; | |
mso-style-unhide:no; | |
mso-style-qformat:yes; | |
mso-style-link:"Heading 1 Char"; | |
margin-top:24.0pt; | |
margin-right:0in; | |
margin-bottom:0in; | |
margin-left:0in; | |
margin-bottom:.0001pt; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
page-break-after:avoid; | |
mso-outline-level:1; | |
font-size:14.0pt; | |
font-family:"Cambria","serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
color:#365F91;} | |
h2 | |
{mso-style-priority:9; | |
mso-style-unhide:no; | |
mso-style-qformat:yes; | |
mso-style-link:"Heading 2 Char"; | |
margin-top:10.0pt; | |
margin-right:0in; | |
margin-bottom:0in; | |
margin-left:0in; | |
margin-bottom:.0001pt; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
page-break-after:avoid; | |
mso-outline-level:2; | |
font-size:13.0pt; | |
font-family:"Cambria","serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
color:#4F81BD;} | |
h3 | |
{mso-style-priority:9; | |
mso-style-unhide:no; | |
mso-style-qformat:yes; | |
mso-style-link:"Heading 3 Char"; | |
margin-top:10.0pt; | |
margin-right:0in; | |
margin-bottom:0in; | |
margin-left:0in; | |
margin-bottom:.0001pt; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
page-break-after:avoid; | |
mso-outline-level:3; | |
font-size:11.0pt; | |
font-family:"Cambria","serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
color:#4F81BD;} | |
p.MsoListBullet, li.MsoListBullet, div.MsoListBullet | |
{mso-style-noshow:yes; | |
mso-style-priority:99; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:10.0pt; | |
margin-left:.25in; | |
text-indent:-.25in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
a:link, span.MsoHyperlink | |
{mso-style-noshow:yes; | |
mso-style-priority:99; | |
color:blue; | |
text-decoration:underline; | |
text-underline:single;} | |
a:visited, span.MsoHyperlinkFollowed | |
{mso-style-noshow:yes; | |
mso-style-priority:99; | |
color:purple; | |
text-decoration:underline; | |
text-underline:single;} | |
p.MsoDocumentMap, li.MsoDocumentMap, div.MsoDocumentMap | |
{mso-style-noshow:yes; | |
mso-style-priority:99; | |
mso-style-link:"Document Map Char"; | |
margin:0in; | |
margin-bottom:.0001pt; | |
mso-pagination:widow-orphan; | |
font-size:8.0pt; | |
font-family:"Tahoma","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast;} | |
p.MsoNoSpacing, li.MsoNoSpacing, div.MsoNoSpacing | |
{mso-style-priority:1; | |
mso-style-unhide:no; | |
mso-style-qformat:yes; | |
margin:0in; | |
margin-bottom:.0001pt; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph | |
{mso-style-priority:34; | |
mso-style-unhide:no; | |
mso-style-qformat:yes; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:10.0pt; | |
margin-left:.5in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
span.Heading1Char | |
{mso-style-name:"Heading 1 Char"; | |
mso-style-priority:9; | |
mso-style-unhide:no; | |
mso-style-locked:yes; | |
mso-style-link:"Heading 1"; | |
font-family:"Cambria","serif"; | |
mso-ascii-font-family:Cambria; | |
mso-hansi-font-family:Cambria; | |
color:#365F91; | |
font-weight:bold;} | |
span.Heading2Char | |
{mso-style-name:"Heading 2 Char"; | |
mso-style-noshow:yes; | |
mso-style-priority:9; | |
mso-style-unhide:no; | |
mso-style-locked:yes; | |
mso-style-link:"Heading 2"; | |
font-family:"Cambria","serif"; | |
mso-ascii-font-family:Cambria; | |
mso-hansi-font-family:Cambria; | |
color:#4F81BD; | |
font-weight:bold;} | |
span.Heading3Char | |
{mso-style-name:"Heading 3 Char"; | |
mso-style-noshow:yes; | |
mso-style-priority:9; | |
mso-style-unhide:no; | |
mso-style-locked:yes; | |
mso-style-link:"Heading 3"; | |
font-family:"Cambria","serif"; | |
mso-ascii-font-family:Cambria; | |
mso-hansi-font-family:Cambria; | |
color:#4F81BD; | |
font-weight:bold;} | |
p.msolistbulletcxspfirst, li.msolistbulletcxspfirst, div.msolistbulletcxspfirst | |
{mso-style-name:msolistbulletcxspfirst; | |
mso-style-unhide:no; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:0in; | |
margin-left:.25in; | |
margin-bottom:.0001pt; | |
text-indent:-.25in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
p.msolistbulletcxspmiddle, li.msolistbulletcxspmiddle, div.msolistbulletcxspmiddle | |
{mso-style-name:msolistbulletcxspmiddle; | |
mso-style-unhide:no; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:0in; | |
margin-left:.25in; | |
margin-bottom:.0001pt; | |
text-indent:-.25in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
p.msolistbulletcxsplast, li.msolistbulletcxsplast, div.msolistbulletcxsplast | |
{mso-style-name:msolistbulletcxsplast; | |
mso-style-unhide:no; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:10.0pt; | |
margin-left:.25in; | |
text-indent:-.25in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
span.DocumentMapChar | |
{mso-style-name:"Document Map Char"; | |
mso-style-noshow:yes; | |
mso-style-priority:99; | |
mso-style-unhide:no; | |
mso-style-locked:yes; | |
mso-style-link:"Document Map"; | |
font-family:"Tahoma","sans-serif"; | |
mso-ascii-font-family:Tahoma; | |
mso-hansi-font-family:Tahoma; | |
mso-bidi-font-family:Tahoma;} | |
p.msolistparagraphcxspfirst, li.msolistparagraphcxspfirst, div.msolistparagraphcxspfirst | |
{mso-style-name:msolistparagraphcxspfirst; | |
mso-style-unhide:no; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:0in; | |
margin-left:.5in; | |
margin-bottom:.0001pt; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
p.msolistparagraphcxspmiddle, li.msolistparagraphcxspmiddle, div.msolistparagraphcxspmiddle | |
{mso-style-name:msolistparagraphcxspmiddle; | |
mso-style-unhide:no; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:0in; | |
margin-left:.5in; | |
margin-bottom:.0001pt; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
p.msolistparagraphcxsplast, li.msolistparagraphcxsplast, div.msolistparagraphcxsplast | |
{mso-style-name:msolistparagraphcxsplast; | |
mso-style-unhide:no; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:10.0pt; | |
margin-left:.5in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:"Calibri","sans-serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
p.Code, li.Code, div.Code | |
{mso-style-name:Code; | |
mso-style-unhide:no; | |
margin-top:0in; | |
margin-right:0in; | |
margin-bottom:10.0pt; | |
margin-left:.5in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:11.0pt; | |
font-family:Consolas; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast; | |
mso-bidi-font-family:"Times New Roman";} | |
p.msopapdefault, li.msopapdefault, div.msopapdefault | |
{mso-style-name:msopapdefault; | |
mso-style-unhide:no; | |
mso-margin-top-alt:auto; | |
margin-right:0in; | |
margin-bottom:10.0pt; | |
margin-left:0in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:12.0pt; | |
font-family:"Times New Roman","serif"; | |
mso-fareast-font-family:"Times New Roman"; | |
mso-fareast-theme-font:minor-fareast;} | |
span.CodeFragment | |
{mso-style-name:"Code Fragment"; | |
mso-style-unhide:no; | |
font-family:Consolas; | |
mso-ascii-font-family:Consolas; | |
mso-hansi-font-family:Consolas;} | |
span.SpellE | |
{mso-style-name:""; | |
mso-spl-e:yes;} | |
span.GramE | |
{mso-style-name:""; | |
mso-gram-e:yes;} | |
.MsoChpDefault | |
{mso-style-type:export-only; | |
mso-default-props:yes; | |
font-size:10.0pt; | |
mso-ansi-font-size:10.0pt; | |
mso-bidi-font-size:10.0pt;} | |
.MsoPapDefault | |
{mso-style-type:export-only; | |
margin-bottom:10.0pt; | |
line-height:115%;} | |
@page Section1 | |
{size:8.5in 11.0in; | |
margin:1.0in .75in 1.0in .75in; | |
mso-header-margin:.5in; | |
mso-footer-margin:.5in; | |
mso-paper-source:0;} | |
div.Section1 | |
{page:Section1;} | |
/* List Definitions */ | |
@list l0 | |
{mso-list-id:-119; | |
mso-list-type:simple; | |
mso-list-template-ids:-1170547422;} | |
@list l0:level1 | |
{mso-level-number-format:bullet; | |
mso-level-style-link:"List Bullet"; | |
mso-level-text:\F0B7; | |
mso-level-tab-stop:.25in; | |
mso-level-number-position:left; | |
margin-left:.25in; | |
text-indent:-.25in; | |
font-family:Symbol;} | |
ol | |
{margin-bottom:0in;} | |
ul | |
{margin-bottom:0in;} | |
--> | |
</style> | |
<!--[if gte mso 10]> | |
<style> | |
/* Style Definitions */ | |
table.MsoNormalTable | |
{mso-style-name:"Table Normal"; | |
mso-tstyle-rowband-size:0; | |
mso-tstyle-colband-size:0; | |
mso-style-noshow:yes; | |
mso-style-priority:99; | |
mso-style-qformat:yes; | |
mso-style-parent:""; | |
mso-padding-alt:0in 5.4pt 0in 5.4pt; | |
mso-para-margin-top:0in; | |
mso-para-margin-right:0in; | |
mso-para-margin-bottom:10.0pt; | |
mso-para-margin-left:0in; | |
line-height:115%; | |
mso-pagination:widow-orphan; | |
font-size:10.0pt; | |
font-family:"Times New Roman","serif";} | |
</style> | |
<![endif]--><!--[if gte mso 9]><xml> | |
<o:shapedefaults v:ext="edit" spidmax="2050"/> | |
</xml><![endif]--><!--[if gte mso 9]><xml> | |
<o:shapelayout v:ext="edit"> | |
<o:idmap v:ext="edit" data="1"/> | |
</o:shapelayout></xml><![endif]--> | |
</head> | |
<body lang=EN-US link=blue vlink=purple style='tab-interval:.5in'> | |
<div class=Section1> | |
<h1><span style='mso-fareast-font-family:"Times New Roman"'>Dynamic Expressions | |
and Queries in LINQ<o:p></o:p></span></h1> | |
<p class=MsoNormal>Database applications frequently rely on “Dynamic | |
SQL”—queries that are constructed at run-time through program logic. The LINQ | |
infrastructure supports similar capabilities through dynamic construction of | |
expression trees using the classes in the <span class=SpellE><span | |
class=CodeFragment>System.Linq.Expressions</span></span> namespace. Expression | |
trees are an appropriate abstraction for a variety of scenarios, but for others | |
a string-based representation may be more convenient. The <a | |
href="#_Dynamic_Expression_API">Dynamic Expression API</a> extends the core | |
LINQ API with that capability. The API is located in the <span class=SpellE>Dynamic.cs</span> | |
source file and provides</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>Dynamic</span> parsing of strings to produce | |
expression trees (the <a href="#_The_ParseLambda_Methods"><span class=SpellE>ParseLambda</span></a> | |
and <a href="#_The_Parse_Method">Parse</a> methods),</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span>Dynamic creation of “Data Classes” (the <a | |
href="#_Dynamic_Class_Creation"><span class=SpellE>CreateClass</span></a> methods), | |
and</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>Dynamic</span> string-based querying through LINQ | |
providers (the <a href="#_IQueryable_Extension_Methods">IQueryable extension | |
methods</a>).</p> | |
<p class=MsoNormal>The Dynamic Expression API relies on a simple <a | |
href="#_Expression_Language">expression language</a> for formulating | |
expressions and queries in strings.</p> | |
<h1><a name="_Dynamic_Expression_API"></a><span style='mso-fareast-font-family: | |
"Times New Roman"'>Dynamic Expression API<o:p></o:p></span></h1> | |
<p class=MsoNormal><a name="_The_ParseLambda_Methods"></a>The Dynamic | |
Expression API is brought into scope by using (importing) the <span | |
class=SpellE><span class=CodeFragment>System.Linq.Dynamic</span></span> | |
namespace. Below is an example of applying the Dynamic Expression API to a LINQ | |
to SQL data source.</p> | |
<p class=Code><span class=SpellE><span class=GramE><span style='color:blue'>var</span></span></span> | |
query =<br> | |
<span class=SpellE>db.Customers</span>.<br> | |
<span class=GramE>Where(</span><span style='color:#A31515'>"City | |
= @0 and <span class=SpellE>Orders.Count</span> >= @1"</span>, <span | |
style='color:#A31515'>"London"</span>, 10).<br> | |
<span class=SpellE><span class=GramE>OrderBy</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"<span class=SpellE>CompanyName</span>"</span>).<br> | |
<span class=GramE>Select(</span><span style='color:#A31515'>"new(<span | |
class=SpellE>CompanyName</span> as Name, Phone)"</span>);</p> | |
<p class=MsoNormal>Note that expressions in the query are strings that could | |
have been dynamically constructed at run-time.</p> | |
<h2><span style='mso-fareast-font-family:"Times New Roman"'>The <span | |
class=SpellE>ParseLambda</span> Methods<o:p></o:p></span></h2> | |
<p class=MsoNormal>The <span class=SpellE><span class=CodeFragment>System.Linq.Dynamic.DynamicExpression</span></span> | |
class defines the following overloaded <span class=SpellE><span | |
class=CodeFragment>ParseLambda</span></span> methods for dynamically parsing | |
and creating lambda expressions.</p> | |
<p class=Code><a name=ParseLambda1></a><span class=GramE><span | |
style='color:blue'>public</span></span> <span style='color:blue'>static</span> <span | |
class=SpellE><span style='color:#2B91AF'>LambdaExpression</span></span> <span | |
class=SpellE>ParseLambda</span>(<br> | |
<span class=SpellE><span style='color:#2B91AF'>ParameterExpression</span></span>[] | |
parameters, <span style='color:#2B91AF'>Type</span> <span class=SpellE>resultType</span>,<br> | |
<span style='color:blue'>string</span> expression, <span | |
class=SpellE><span style='color:blue'>params</span></span> <span | |
style='color:blue'>object</span>[] values);</p> | |
<p class=Code><a name=ParseLambda2></a><span class=GramE><span | |
style='color:blue'>public</span></span> <span style='color:blue'>static</span> <span | |
class=SpellE><span style='color:#2B91AF'>LambdaExpression</span></span> <span | |
class=SpellE>ParseLambda</span>(<br> | |
<span style='color:#2B91AF'>Type</span> <span class=SpellE>argumentType</span>, | |
<span style='color:#2B91AF'>Type</span> <span class=SpellE>resultType</span>,<br> | |
<span style='color:blue'>string</span> expression, <span | |
class=SpellE><span style='color:blue'>params</span></span> <span | |
style='color:blue'>object</span>[] values);</p> | |
<p class=Code><a name=ParseLambda3></a><span class=GramE><span | |
style='color:blue'>public</span></span> <span style='color:blue'>static</span> <span | |
style='color:#2B91AF'>Expression</span><<span class=SpellE><span | |
style='color:#2B91AF'>Func</span></span><<span class=SpellE>TArgument</span>, | |
<span class=SpellE>TResult</span>>><br> | |
<span class=SpellE>ParseLambda</span><<span class=SpellE>TArgument</span>, | |
<span class=SpellE>TResult</span>>(<br> | |
<span style='color:blue'>string</span> | |
expression, <span class=SpellE><span style='color:blue'>params</span></span> <span | |
style='color:blue'>object</span>[] values);</p> | |
<p class=MsoNormal>The <a href="#ParseLambda1">first</a> <span class=SpellE><span | |
class=CodeFragment>ParseLambda</span></span> overload parses a lambda | |
expression with the given <span class=CodeFragment>parameters</span> and <span | |
class=CodeFragment>expression</span> body and returns an <span | |
class=CodeFragment>Expression<<span class=SpellE>Func</span><…>></span> | |
instance representing the result. If the <span class=SpellE><span | |
class=CodeFragment>resultType</span></span> parameter is non-null it specifies | |
the required result type for the expression. The <span class=CodeFragment>values</span> | |
parameter supplies zero or more <a href="#_Substitution_Values">substitution | |
values</a> that may be referenced in the expression.</p> | |
<p class=MsoNormal><a name=LambdaExample1></a>The example</p> | |
<p class=Code><span class=SpellE><span style='color:#2B91AF'>ParameterExpression</span></span> | |
x = <span class=SpellE><span style='color:#2B91AF'>Expression</span>.Parameter</span>(<span | |
class=SpellE><span style='color:blue'>typeof</span></span>(<span class=SpellE><span | |
style='color:blue'>int</span></span>), <span style='color:#A31515'>"x"</span>);<br> | |
<span class=SpellE><span style='color:#2B91AF'>ParameterExpression</span></span> | |
y = <span class=SpellE><span style='color:#2B91AF'>Expression</span>.Parameter</span>(<span | |
class=SpellE><span style='color:blue'>typeof</span></span>(<span class=SpellE><span | |
style='color:blue'>int</span></span>), <span style='color:#A31515'>"y"</span>);<br> | |
<span class=SpellE><span style='color:#2B91AF'>LambdaExpression</span></span> e | |
= <span class=SpellE><span style='color:#2B91AF'>DynamicExpression</span>.ParseLambda</span>(<br> | |
<span style='color:blue'>new</span> <span class=SpellE><span | |
style='color:#2B91AF'>ParameterExpression</span></span>[] { x, y }, <span | |
style='color:blue'>null</span>, <span style='color:#A31515'>"(x + y) * | |
2"</span>);</p> | |
<p class=MsoNormal><span class=GramE>creates</span> and assigns an <span | |
class=CodeFragment>Expression<<span class=SpellE>Func</span><<span | |
class=SpellE>int</span>, <span class=SpellE>int</span>, <span class=SpellE>int</span>>></span> | |
instance to <span class=CodeFragment>e</span> representing the expression <span | |
class=CodeFragment>(x</span> <span class=CodeFragment>+</span> <span | |
class=CodeFragment>y)</span> <span class=CodeFragment>*</span> <span | |
class=CodeFragment>2</span>. If a required result type is specified, as in</p> | |
<p class=Code><span class=SpellE><span style='color:#2B91AF'>LambdaExpression</span></span> | |
e = <span class=SpellE><span class=GramE><span style='color:#2B91AF'>DynamicExpression</span>.ParseLambda</span></span><span | |
class=GramE>(</span><br> | |
<span style='color:blue'>new</span> <span class=SpellE><span | |
style='color:#2B91AF'>ParameterExpression</span></span>[] { x, y }, <span | |
class=SpellE><span style='color:blue'>typeof</span></span>(<span | |
style='color:blue'>double</span>), <span style='color:#A31515'>"(x + y) * | |
2"</span>);</p> | |
<p class=MsoNormal><span class=GramE>the</span> parsing operation will include | |
an <a href="#_Conversions">implicit conversion</a> to the given result type, in | |
this case yielding an <span class=CodeFragment>Expression<<span | |
class=SpellE>Func</span><<span class=SpellE>int</span>, <span class=SpellE>int</span>, | |
double>></span> instance.</p> | |
<p class=MsoNormal>The <a href="#ParseLambda2">second</a> <span class=SpellE><span | |
class=CodeFragment>ParseLambda</span></span> overload parses a lambda | |
expression with a single unnamed parameter of a specified <span class=SpellE><span | |
class=CodeFragment>argumentType</span></span>. This method corresponds to | |
calling the first <span class=SpellE>ParseLambda</span> overload with a <span | |
class=CodeFragment>parameters</span> argument containing a single <span | |
class=SpellE><span class=CodeFragment>ParameterExpression</span></span> with an | |
empty or null <span class=CodeFragment>Name</span> property.</p> | |
<p class=MsoNormal>When parsing a lambda expression with a single unnamed | |
parameter, the members of the unnamed parameter are automatically in scope in | |
the expression string, and the <a href="#_Current_Instance">current instance</a> | |
given by the unnamed parameter can be referenced in whole using the keyword <span | |
class=CodeFragment>it</span>. The example</p> | |
<p class=Code><span class=SpellE><span style='color:#2B91AF'>LambdaExpression</span></span> | |
e = <span class=SpellE><span class=GramE><span style='color:#2B91AF'>DynamicExpression</span>.ParseLambda</span></span><span | |
class=GramE>(</span><br> | |
<span class=SpellE><span style='color:blue'>typeof</span></span>(<span | |
style='color:#2B91AF'>Customer</span>), <span class=SpellE><span | |
style='color:blue'>typeof</span></span>(<span class=SpellE><span | |
style='color:blue'>bool</span></span>),<br> | |
<span style='color:#A31515'>"City = @0 and <span | |
class=SpellE>Orders.Count</span> >= @1"</span>,<br> | |
<span style='color:#A31515'>"London"</span>, 10);</p> | |
<p class=MsoNormal><span class=GramE>creates</span> and assigns an <span | |
class=CodeFragment>Expression<<span class=SpellE>Func</span><Customer, <span | |
class=SpellE>bool</span>>></span> instance to <span class=CodeFragment>e</span>. | |
Note that <span class=CodeFragment>City</span> and <span class=CodeFragment>Orders</span> | |
are members of <span class=CodeFragment>Customer</span> that are automatically | |
in scope. Also note the use of <a href="#_Substitution_Values">substitution | |
values</a> to supply the constant values <span class=CodeFragment>"London"</span> | |
and <span class=CodeFragment>10</span>.</p> | |
<p class=MsoNormal>The <a href="#ParseLambda3">third</a> <span class=SpellE><span | |
class=CodeFragment>ParseLambda</span></span> overload is a <span class=SpellE>genericly</span> | |
typed version of the second overload. The example below produces the same <span | |
class=CodeFragment>Expression<<span class=SpellE>Func</span><Customer, <span | |
class=SpellE>bool</span>>></span> instance as the example above, but is | |
statically typed to that exact type.</p> | |
<p class=Code><span style='color:#2B91AF'>Expression</span><<span | |
class=SpellE><span style='color:#2B91AF'>Func</span></span><<span | |
style='color:#2B91AF'>Customer</span>, <span class=SpellE><span | |
style='color:blue'>bool</span></span>>> e =<br> | |
<span class=SpellE><span style='color:#2B91AF'>DynamicExpression</span>.ParseLambda</span><<span | |
style='color:#2B91AF'>Customer</span>, <span class=SpellE><span | |
style='color:blue'>bool</span></span><span class=GramE>>(</span><br> | |
<span style='color:#A31515'>"City | |
= @0 and <span class=SpellE>Orders.Count</span> >= @1"</span>,<br> | |
<span style='color:#A31515'>"London"</span>, | |
10);</p> | |
<h2><a name="_The_Parse_Method"></a><span style='mso-fareast-font-family:"Times New Roman"'>The | |
Parse Method<o:p></o:p></span></h2> | |
<p class=MsoNormal>The <span class=SpellE><span class=CodeFragment>System.Linq.Dynamic.DynamicExpression</span></span> | |
class defines the following method for parsing and creating expression tree | |
fragments.</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>Expression</span> | |
Parse(<span style='color:#2B91AF'>Type</span> <span class=SpellE>resultType</span>, | |
<span style='color:blue'>string</span> expression,<br> | |
<span class=SpellE><span style='color:blue'>params</span></span> | |
<span style='color:blue'>object</span>[] values);</p> | |
<p class=MsoNormal>The Parse method parses the given <span class=CodeFragment>expression</span> | |
and returns an expression tree. If the <span class=SpellE><span | |
class=CodeFragment>resultType</span></span> parameter is non-null it specifies | |
the required result type of the expression. The <span class=CodeFragment>values</span> | |
parameter supplies zero or more <a href="#_Substitution_Values">substitution | |
values</a> that may be referenced in the expression.</p> | |
<p class=MsoNormal>Unlike the <span class=SpellE><span class=CodeFragment>ParseLambda</span></span> | |
methods, the <span class=CodeFragment>Parse</span> method returns an “unbound” | |
expression tree fragment. The following example uses <span class=GramE><span | |
class=CodeFragment>Parse</span></span> to produce the same result as a <a | |
href="#LambdaExample1">previous example</a>:</p> | |
<p class=Code><span class=SpellE><span style='color:#2B91AF'>ParameterExpression</span></span> | |
x = <span class=SpellE><span style='color:#2B91AF'>Expression</span>.Parameter</span>(<span | |
class=SpellE><span style='color:blue'>typeof</span></span>(<span class=SpellE><span | |
style='color:blue'>int</span></span>), <span style='color:#A31515'>"x"</span>);<br> | |
<span class=SpellE><span style='color:#2B91AF'>ParameterExpression</span></span> | |
y = <span class=SpellE><span style='color:#2B91AF'>Expression</span>.Parameter</span>(<span | |
class=SpellE><span style='color:blue'>typeof</span></span>(<span class=SpellE><span | |
style='color:blue'>int</span></span>), <span style='color:#A31515'>"y"</span>);<br> | |
<span style='color:#2B91AF'>Dictionary</span><<span style='color:blue'>string</span>, | |
<span style='color:blue'>object</span>> symbols = <span style='color:blue'>new</span> | |
<span style='color:#2B91AF'>Dictionary</span><<span style='color:blue'>string</span>, | |
<span style='color:blue'>object</span>>();<br> | |
<span class=SpellE>symbols.Add</span>(<span style='color:#A31515'>"x"</span>, | |
x);<br> | |
<span class=SpellE>symbols.Add</span>(<span style='color:#A31515'>"y"</span>, | |
y);<br> | |
<span style='color:#2B91AF'>Expression</span> body = <span class=SpellE><span | |
style='color:#2B91AF'>DynamicExpression</span>.Parse</span>(<span | |
style='color:blue'>null</span>, <span style='color:#A31515'>"(x + y) * | |
2"</span>, symbols);<br> | |
<span class=SpellE><span style='color:#2B91AF'>LambdaExpression</span></span> e | |
= <span class=SpellE><span style='color:#2B91AF'>Expression</span>.Lambda</span>(<br> | |
body, <span style='color:blue'>new</span> <span | |
class=SpellE><span style='color:#2B91AF'>ParameterExpression</span></span>[] { | |
x, y });</p> | |
<p class=MsoNormal>Note the use of a <span class=CodeFragment>Dictionary<string, | |
object></span> to provide a dictionary of named <a | |
href="#_Substitution_Values">substitution values</a> that can be referenced in | |
the expression.</p> | |
<h2><a name="_Substitution_Values"></a><span style='mso-fareast-font-family: | |
"Times New Roman"'>Substitution Values<o:p></o:p></span></h2> | |
<p class=MsoNormal>Several methods in the Dynamic Expression API permit <em><span | |
style='font-family:"Calibri","sans-serif"'>substitution values</span></em> to | |
be specified through a parameter array. Substitution values are referenced in | |
an expression using <a href="#_Identifiers">identifiers</a> of the form <span | |
class=CodeFragment>@x</span>, where <span class=CodeFragment>x</span> is an | |
index into the parameter array. The last element of the parameter array may be | |
an object that implements <span class=SpellE><span class=CodeFragment>IDictionary</span></span><span | |
class=CodeFragment><string, object></span>. If so, this dictionary is | |
used to map identifiers to substitution values during parsing.</p> | |
<p class=MsoNormal>An identifier that references a substitution value is | |
processed as follows:</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>If</span> the value is of type <span class=SpellE><span | |
class=CodeFragment>System.Linq.Expressions.LambdaExpression</span></span>, the | |
identifier must occur as part of a <a href="#_Dynamic_Expression_Invocation">dynamic | |
lambda invocation</a>. This allows composition of dynamic lambda expressions.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span>Otherwise, if the value is of type <span class=SpellE><span | |
class=CodeFragment>System.Linq.Expressions.Expression</span></span>, the given | |
expression is substituted for the identifier.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span>Otherwise, the <span class=SpellE><span class=CodeFragment>Expression.Constant</span></span><span | |
class=CodeFragment> </span>method is used to create a constant expression from | |
the value which is then substituted for the identifier.</p> | |
<h2><a name="_Dynamic_Class_Creation"></a><a name="_Dynamic_Data_Classes"></a><span | |
style='mso-fareast-font-family:"Times New Roman"'>Dynamic Data Classes<o:p></o:p></span></h2> | |
<p class=MsoNormal>A data class is a class that contains only data members. The | |
<span class=SpellE><span class=CodeFragment>System.Linq.Dynamic.DynamicExpression</span></span> | |
class defines the following methods for dynamically creating data classes.</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>Type</span> <span | |
class=SpellE>CreateClass</span>(<span class=SpellE><span style='color:blue'>params</span></span> | |
<span class=SpellE><span style='color:#2B91AF'>DynamicProperty</span></span>[] | |
properties);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>Type</span> <span | |
class=SpellE>CreateClass</span>(<span class=SpellE><span style='color:#2B91AF'>IEnumerable</span></span><<span | |
class=SpellE><span style='color:#2B91AF'>DynamicProperty</span></span>> | |
properties);</p> | |
<p class=MsoNormal>The <span class=SpellE><span class=CodeFragment>CreateClass</span></span> | |
method creates a new data class with a given set of public properties and | |
returns the <span class=SpellE><span class=CodeFragment>System.Type</span></span> | |
object for the newly created class. If a data class with an identical sequence | |
of properties has already been created, the <span class=SpellE><span | |
class=CodeFragment>System.Type</span></span> object for this class is returned.</p> | |
<p class=MsoNormal>Data classes implement private instance variables and | |
read/write property <span class=SpellE>accessors</span> for the specified | |
properties. Data classes also override the <span class=CodeFragment>Equals</span> | |
and <span class=SpellE><span class=CodeFragment>GetHashCode</span></span> | |
members to implement by-value equality.</p> | |
<p class=MsoNormal>Data classes are created in an in-memory assembly in the | |
current application domain. All data classes inherit from <span class=SpellE><span | |
class=CodeFragment>System.Linq.Dynamic.DynamicClass</span></span> and are given | |
automatically generated names that should be considered private (the names will | |
be unique within the application domain but not across multiple invocations of | |
the application). Note that once created, a data class stays in memory for the | |
lifetime of the current application domain. There is currently no way to unload | |
a dynamically created data class.</p> | |
<p class=MsoNormal>The dynamic expression parser uses the <span class=SpellE><span | |
class=CodeFragment>CreateClass</span></span> methods to generate classes from <a | |
href="#_Anonymous_Object_Initializer">data object initializers</a>. This | |
feature in turn is often used with the dynamic <span class=CodeFragment>Select</span> | |
method to create projections.</p> | |
<p class=MsoNormal>The example below uses <span class=SpellE><span | |
class=CodeFragment>CreateClass</span></span> to create a data class with two | |
properties, <span class=CodeFragment>Name</span> and <span class=CodeFragment>Birthday</span>, | |
and then uses .NET reflection to create an instance of the class and assign | |
values to the properties.</p> | |
<p class=Code><span class=SpellE><span style='color:#2B91AF'>DynamicProperty</span></span>[] | |
props = <span style='color:blue'>new</span> <span class=SpellE><span | |
style='color:#2B91AF'>DynamicProperty</span></span>[] {<br> | |
<span style='color:blue'>new</span> <span class=SpellE><span | |
style='color:#2B91AF'>DynamicProperty</span></span>(<span style='color:#A31515'>"Name"</span>, | |
<span class=SpellE><span style='color:blue'>typeof</span></span>(<span | |
style='color:blue'>string</span>)),<br> | |
<span style='color:blue'>new</span> <span class=SpellE><span | |
style='color:#2B91AF'>DynamicProperty</span></span>(<span style='color:#A31515'>"Birthday"</span>, | |
<span class=SpellE><span style='color:blue'>typeof</span></span>(<span | |
class=SpellE><span style='color:#2B91AF'>DateTime</span></span>)) };<br> | |
<span style='color:#2B91AF'>Type</span> <span class=SpellE>type</span> = <span | |
class=SpellE><span style='color:#2B91AF'>DynamicExpression</span>.CreateClass</span>(props);<br> | |
<span style='color:blue'>object</span> <span class=SpellE>obj</span> = <span | |
class=SpellE><span style='color:#2B91AF'>Activator</span>.CreateInstance</span>(type);<br> | |
<span class=SpellE>t.GetProperty</span>(<span style='color:#A31515'>"Name"</span>).<span | |
class=SpellE>SetValue</span>(<span class=SpellE>obj</span>, <span | |
style='color:#A31515'>"Albert"</span>, <span style='color:blue'>null</span>);<br> | |
<span class=SpellE>t.GetProperty</span>(<span style='color:#A31515'>"Birthday"</span>).<span | |
class=SpellE>SetValue</span>(<span class=SpellE>obj</span>, <span | |
style='color:blue'>new</span> <span class=SpellE><span style='color:#2B91AF'>DateTime</span></span>(1879, | |
3, 14), <span style='color:blue'>null</span>);<br> | |
<span class=SpellE><span style='color:#2B91AF'>Console</span>.WriteLine</span>(<span | |
class=SpellE>obj</span>);</p> | |
<h2><a name="_IQueryable_Extension_Methods"></a><span style='mso-fareast-font-family: | |
"Times New Roman"'>IQueryable Extension Methods<o:p></o:p></span></h2> | |
<p class=MsoNormal>The <span class=SpellE><span class=CodeFragment>System.Linq.Dynamic.DynamicQueryable</span></span> | |
class implements the following extension methods for dynamically querying | |
objects that implement the <span class=CodeFragment>IQueryable<T></span> | |
interface.</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>IQueryable</span> | |
Where(<span style='color:blue'>this</span> <span style='color:#2B91AF'>IQueryable</span> | |
source,<br> | |
<span style='color:blue'>string</span> predicate, <span | |
class=SpellE><span style='color:blue'>params</span></span> <span | |
style='color:blue'>object</span>[] values);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>IQueryable</span><T> | |
Where<T>(<span style='color:blue'>this</span> <span style='color:#2B91AF'>IQueryable</span><T> | |
source,<br> | |
<span style='color:blue'>string</span> predicate, <span | |
class=SpellE><span style='color:blue'>params</span></span> <span | |
style='color:blue'>object</span>[] values);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>IQueryable</span> | |
Select(<span style='color:blue'>this</span> <span style='color:#2B91AF'>IQueryable</span> | |
source,<br> | |
<span style='color:blue'>string</span> selector, <span | |
class=SpellE><span style='color:blue'>params</span></span> <span | |
style='color:blue'>object</span>[] values);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>IQueryable</span> <span | |
class=SpellE>OrderBy</span>(<span style='color:blue'>this</span> <span | |
style='color:#2B91AF'>IQueryable</span> source,<br> | |
<span style='color:blue'>string</span> ordering, <span | |
class=SpellE><span style='color:blue'>params</span></span> <span | |
style='color:blue'>object</span>[] values);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>IQueryable</span><T> | |
<span class=SpellE>OrderBy</span><T>(<span style='color:blue'>this</span> | |
<span style='color:#2B91AF'>IQueryable</span><T> source,<br> | |
<span style='color:blue'>string</span> ordering, <span | |
class=SpellE><span style='color:blue'>params</span></span> <span | |
style='color:blue'>object</span>[] values);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>IQueryable</span> | |
Take(<span style='color:blue'>this</span> <span style='color:#2B91AF'>IQueryable</span> | |
source, <span class=SpellE><span style='color:blue'>int</span></span> count);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>IQueryable</span> | |
Skip(<span style='color:blue'>this</span> <span style='color:#2B91AF'>IQueryable</span> | |
source, <span class=SpellE><span style='color:blue'>int</span></span> count);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span style='color:#2B91AF'>IQueryable</span> <span | |
class=SpellE>GroupBy</span>(<span style='color:blue'>this</span> <span | |
style='color:#2B91AF'>IQueryable</span> source,<br> | |
<span style='color:blue'>string</span> <span class=SpellE>keySelector</span>, | |
<span style='color:blue'>string</span> <span class=SpellE>elementSelector</span>, | |
<span class=SpellE><span style='color:blue'>params</span></span> <span | |
style='color:blue'>object</span>[] values);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span class=SpellE><span style='color:blue'>bool</span></span> | |
Any(<span style='color:blue'>this</span> <span style='color:#2B91AF'>IQueryable</span> | |
source);</p> | |
<p class=Code><span class=GramE><span style='color:blue'>public</span></span> <span | |
style='color:blue'>static</span> <span class=SpellE><span style='color:blue'>int</span></span> | |
Count(<span style='color:blue'>this</span> <span style='color:#2B91AF'>IQueryable</span> | |
source);</p> | |
<p class=MsoNormal>These methods correspond to their <span class=SpellE><span | |
class=CodeFragment>System.Linq.Queryable</span></span> counterparts, except | |
that they operate on <span class=CodeFragment>IQueryable</span> instead of <span | |
class=CodeFragment>IQueryable<T></span> and use strings instead of lambda | |
expressions to express predicates, selectors, and orderings. <span | |
class=CodeFragment>IQueryable</span> is the non-generic base interface for <span | |
class=CodeFragment>IQueryable<T></span>, so the methods can be used even | |
when <span class=CodeFragment>T</span> isn’t known on beforehand, i.e. when the | |
source of a query is dynamically determined. (Note that because a dynamic | |
predicate or ordering does not affect the result type, generic overloads are | |
provided for <span class=CodeFragment>Where</span> and <span class=SpellE><span | |
class=CodeFragment>OrderBy</span></span> in order to preserve strong typing | |
when possible.)</p> | |
<p class=MsoNormal>The <span class=CodeFragment>predicate</span>, <span | |
class=CodeFragment>selector</span>, <span class=CodeFragment>ordering</span>, <span | |
class=SpellE><span class=CodeFragment>keySelector</span></span>, and <span | |
class=SpellE><span class=CodeFragment>elementSelector</span></span> parameters | |
are strings containing expressions written in the <a | |
href="#_Expression_Language">expression language</a>. In the expression | |
strings, the members of the <a href="#_Current_Instance">current instance</a> | |
are automatically in scope and the instance itself can be referenced using the | |
keyword <span class=CodeFragment>it</span>.</p> | |
<p class=MsoNormal>The <span class=SpellE><span class=CodeFragment>OrderBy</span></span> | |
method permits a sequence of orderings to be specified, separated by commas. | |
Each ordering may optionally be followed by <span class=SpellE><span | |
class=CodeFragment>asc</span></span> or <span class=CodeFragment>ascending</span> | |
to indicate ascending <span class=GramE>order,</span> or <span class=SpellE><span | |
class=CodeFragment>desc</span></span> or <span class=CodeFragment>descending</span> | |
to indicate descending order. The default order is ascending. The example</p> | |
<p class=Code><span class=SpellE><span class=GramE>products.OrderBy</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"<span class=SpellE>Category.CategoryName</span>, | |
<span class=SpellE>UnitPrice</span> descending"</span>);</p> | |
<p class=MsoNormal><span class=GramE>orders</span> a sequence of products by | |
ascending category name and, within each category, descending unit price.</p> | |
<h2><span style='mso-fareast-font-family:"Times New Roman"'>The <span | |
class=SpellE>ParseException</span> Class<o:p></o:p></span></h2> | |
<p class=MsoNormal>The Dynamic Expression API reports parsing errors using the <span | |
class=SpellE><span class=CodeFragment>System.Linq.Dynamic.ParseException</span></span> | |
class. The <span class=CodeFragment>Position</span> property of the <span | |
class=SpellE><span class=CodeFragment>ParseException</span></span> class gives | |
the character index in the expression string at which the parsing error | |
occurred.</p> | |
<h1><a name="_Expression_Language"></a><span style='mso-fareast-font-family: | |
"Times New Roman"'>Expression Language<o:p></o:p></span></h1> | |
<p class=MsoNormal>The expression language implemented by the Dynamic | |
Expression API provides a simple and convenient way of writing expressions that | |
can be parsed into LINQ expression trees. The language supports most of the | |
constructs of expression trees, but it is by no means a complete query or | |
programming language. In particular, the expression language does not support | |
statements or declarations.</p> | |
<p class=MsoNormal>The expression language is designed to be familiar to C#, | |
VB, and SQL users. For this reason, some operators are present in multiple | |
forms, such as <span class=CodeFragment>&&</span> and <span | |
class=SpellE><span class=CodeFragment>and</span></span>.</p> | |
<h2><a name="_Identifiers"></a><span style='mso-fareast-font-family:"Times New Roman"'>Identifiers<o:p></o:p></span></h2> | |
<p class=MsoNormal>An Identifier consists of a letter or underscore followed by | |
any number of letters, digits, or underscores. In order to reference an | |
identifier with the same spelling as a keyword, the identifier must be prefixed | |
with a single @ character. Some examples of identifiers:</p> | |
<p class=Code><span class=GramE>x</span> Hello | |
m_1 @true @String</p> | |
<p class=MsoNormal>Identifiers of the from @x, where x is an integral number | |
greater than or equal to zero, are used to denote the <a | |
href="#_Substitution_Values">substitution values</a>, if any, that were passed | |
to the expression parser. For example:</p> | |
<p class=Code><span class=SpellE><span class=GramE>customers.Where</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"Country = @0"</span>, | |
country);</p> | |
<p class=MsoNormal>Casing is not significant in identifiers or keywords.</p> | |
<h2><span style='mso-fareast-font-family:"Times New Roman"'>Literals<o:p></o:p></span></h2> | |
<p class=MsoNormal>The expression language supports integer, real, string, and | |
character literals.</p> | |
<p class=MsoNormal>An <em><span style='font-family:"Calibri","sans-serif"'>integer | |
literal</span></em> consists of a sequence of digits. The type of an integer | |
literal is the first of the types <span class=CodeFragment>Int32</span>, <span | |
class=CodeFragment>UInt32</span>, <span class=CodeFragment>Int64</span>, or <span | |
class=CodeFragment>UInt64</span> that can represent the given value. An integer | |
literal implicitly converts to any other <a href="#NumericTypes">numeric type</a> | |
provided the number is in the range of that type. Some examples of integer | |
literals:</p> | |
<p class=Code>0 123 10000</p> | |
<p class=MsoNormal>A <em><span style='font-family:"Calibri","sans-serif"'>real | |
literal</span></em> consists of an integral part followed by a fractional part | |
and/or an exponent. The integral part is a sequence of one or more digits. The | |
fractional part is a decimal point followed by one or more digits. The exponent | |
is the letter <span class=CodeFragment>e</span> or <span class=CodeFragment>E</span> | |
followed by an optional <span class=CodeFragment>+</span> or <span | |
class=CodeFragment>–</span> sign followed by one or more digits. The type of a | |
real literal is <span class=GramE><span class=CodeFragment>Double</span></span>. | |
A real literal implicitly converts to any other <a href="#RealTypes">real type</a> | |
provided the number is in the range of that type. Some examples of real | |
literals:</p> | |
<p class=Code>1.0 2.25 10000.0 | |
1e0 1e10 1.2345E-4</p> | |
<p class=MsoNormal>A <em><span style='font-family:"Calibri","sans-serif"'>string | |
literal</span></em> consists of zero or more characters enclosed in double | |
quotes. Inside a string literal, a double quote is written as two consecutive | |
double quotes. The type of a string literal is <span class=CodeFragment>String</span>. | |
Some examples of string literals:</p> | |
<p class=Code>"<span class=GramE>hello</span>" | |
"" | |
"""quoted""" "'"</p> | |
<p class=MsoNormal>A <em><span style='font-family:"Calibri","sans-serif"'>character | |
literal</span></em> consists of a single character enclosed in single quotes. | |
Inside a character literal, a single quote is written as two consecutive single | |
quotes. The type of a character literal is <span class=CodeFragment>Char</span>. | |
Some examples of character literals:</p> | |
<p class=Code>'A' '1' '''' '"'</p> | |
<h2><span style='mso-fareast-font-family:"Times New Roman"'>Constants<o:p></o:p></span></h2> | |
<p class=MsoNormal>The predefined constants <span class=CodeFragment>true</span> | |
and <span class=CodeFragment>false</span> denote the two values of the type <span | |
class=CodeFragment>Boolean</span>.</p> | |
<p class=MsoNormal>The predefined constant <span class=CodeFragment>null</span> | |
denotes a null reference. The <span class=CodeFragment>null</span> constant is | |
of type <span class=CodeFragment>Object</span>, but is also implicitly | |
convertible to any reference type.</p> | |
<h2><a name="_Predefined_types"></a><a name="_Types"></a><span | |
style='mso-fareast-font-family:"Times New Roman"'>Types<o:p></o:p></span></h2> | |
<p class=MsoNormal>The expression language defines the following <em><span | |
style='font-family:"Calibri","sans-serif"'>primitive types</span></em>:</p> | |
<p class=MsoNormal style='margin-left:.5in'><span class=CodeFragment>Object</span> | |
<span class=CodeFragment>Boolean</span> | |
<span class=CodeFragment>Char</span> | |
<span class=CodeFragment>String</span> | |
<span class=SpellE><span class=CodeFragment>SByte</span></span> | |
<span class=CodeFragment>Byte</span><br> | |
<span class=CodeFragment>Int16</span> | |
<span class=CodeFragment>UInt16</span> | |
<span class=CodeFragment>Int32</span> | |
<span class=CodeFragment>UInt32</span> | |
<span class=CodeFragment>Int64</span> | |
<span class=CodeFragment>UInt64</span><br> | |
<span class=CodeFragment>Decimal Single</span> | |
<span class=CodeFragment>Double</span> | |
<span class=SpellE><span class=CodeFragment>DateTime</span></span> | |
<span class=SpellE><span class=CodeFragment>TimeSpan</span></span> | |
<span class=SpellE><span class=CodeFragment>Guid</span></span></p> | |
<p class=MsoNormal>The primitive types correspond to the similarly named types | |
in the System namespace of the .NET Framework Base Class Library. The | |
expression language also defines a set of <em><span style='font-family:"Calibri","sans-serif"'>accessible | |
types</span></em> consisting of the primitive types and the following types | |
from the System namespace:</p> | |
<p class=MsoNormal style='margin-left:.5in'><span class=CodeFragment>Math</span> | |
<span class=CodeFragment>Convert</span></p> | |
<p class=MsoNormal>The accessible types are the only types that can be | |
explicitly referenced in expressions, and method invocations in the expression | |
language are restricted to methods declared in the accessible types.</p> | |
<p class=MsoNormal>The <span class=SpellE><em><span style='font-family:"Calibri","sans-serif"'>nullable</span></em></span><em><span | |
style='font-family:"Calibri","sans-serif"'> form</span></em> of a value type is | |
referenced by writing <span class=GramE>a <span class=CodeFragment>?</span></span> | |
<span class=GramE>after</span> the type name. <span class=GramE>For example, <span | |
class=CodeFragment>Int32?</span></span> <span class=GramE>denotes</span> the <span | |
class=SpellE>nullable</span> form of <span class=CodeFragment>Int32</span>.</p> | |
<p class=MsoNormal><a name=IntegralTypes></a>The non-<span class=SpellE>nullable</span> | |
and <span class=SpellE>nullable</span> forms of the types <span class=SpellE><span | |
class=CodeFragment>SByte</span></span>, <span class=CodeFragment>Byte</span>, <span | |
class=CodeFragment>Int16</span>, <span class=CodeFragment>UInt16</span>, <span | |
class=CodeFragment>Int32</span>, <span class=CodeFragment>UInt32</span>, <span | |
class=CodeFragment>Int64</span>, and <span class=CodeFragment>UInt64</span> are | |
collectively called the <em><span style='font-family:"Calibri","sans-serif"'>integral | |
types</span></em>. </p> | |
<p class=MsoNormal><a name="_Conversions"></a><a name=RealTypes></a>The non-<span | |
class=SpellE>nullable</span> and <span class=SpellE>nullable</span> forms of | |
the types <span class=CodeFragment>Single</span>, <span class=CodeFragment>Double</span>, | |
and <span class=CodeFragment>Decimal</span> are collectively called the <em><span | |
style='font-family:"Calibri","sans-serif"'>real types</span></em>.</p> | |
<p class=MsoNormal><a name=NumericTypes></a>The integral types and real types | |
are collectively called the <em><span style='font-family:"Calibri","sans-serif"'>numeric | |
types</span></em>.</p> | |
<h2><a name="_Conversions_1"></a><span style='mso-fareast-font-family:"Times New Roman"'>Conversions<o:p></o:p></span></h2> | |
<p class=MsoNormal>The following conversions are implicitly performed by the | |
expression language:</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>From</span> the <span class=SpellE>the</span> <span | |
class=CodeFragment>null</span> literal to any reference type or <span | |
class=SpellE>nullable</span> type.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>From</span> an integer literal to an <a | |
href="#IntegralTypes">integral type</a> or <a href="#RealTypes">real type</a> | |
provided the number is within the range of that type.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>From</span> a real literal to a <a href="#RealTypes">real | |
type</a> provided the number is within the range of that type.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>From</span> a string literal to an <span class=SpellE>enum</span> | |
type provided the string literal contains the name of a member of that <span | |
class=SpellE>enum</span> type.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>From</span> a source type that is assignment | |
compatible with the target type according to the <span class=SpellE><span | |
class=CodeFragment>Type.IsAssignableFrom</span></span> method in .NET.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>From</span> a non-<span class=SpellE>nullable</span> | |
value type to the <span class=SpellE>nullable</span> form of that value type.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>From</span> a <a href="#NumericTypes">numeric type</a> | |
to another numeric type with greater range.</p> | |
<p class=MsoNormal>The expression language permits explicit conversions using | |
the syntax <span class=GramE><i>type</i><span class=CodeFragment>(</span></span><span | |
class=SpellE><i>expr</i></span><span class=CodeFragment>)</span>, where <i>type</i> | |
is a type name optionally followed by <span class=CodeFragment>?</span> <span | |
class=GramE>and</span> <span class=SpellE><i>expr</i></span> is an expression. | |
This syntax may be used to perform the following conversions:</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>Between</span> two types provided <span class=SpellE><span | |
class=CodeFragment>Type.IsAssignableFrom</span></span> is true in one or both | |
directions.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>Between</span> two types provided one or both are | |
interface types.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span><span class=GramE>Between</span> the <span class=SpellE>nullable</span> | |
and non-<span class=SpellE>nullable</span> forms of any value type.</p> | |
<p class=MsoListParagraph style='text-indent:-.25in'><span style='font-family: | |
Symbol'>·</span><span style='font-size:7.0pt;line-height:115%;font-family:"Times New Roman","serif"'> | |
</span>Between any two types belonging to the set consisting of <span | |
class=SpellE><span class=CodeFragment>SByte</span></span>, <span | |
class=CodeFragment>Byte</span>, <span class=CodeFragment>Int16</span>, <span | |
class=CodeFragment>UInt16</span>, <span class=CodeFragment>Int32</span>, <span | |
class=CodeFragment>UInt32</span>, <span class=CodeFragment>Int64</span>, <span | |
class=CodeFragment>UInt64</span>, <span class=CodeFragment>Decimal</span>, <span | |
class=CodeFragment>Single</span>, <span class=CodeFragment>Double</span>, <span | |
class=CodeFragment>Char</span>, any <span class=SpellE>enum</span> type, as | |
well as the <span class=SpellE>nullable</span> forms of those types.</p> | |
<h2 style='line-height:150%'><span style='mso-fareast-font-family:"Times New Roman"'>Operators<o:p></o:p></span></h2> | |
<p class=MsoNormal>The table below shows the operators supported by the | |
expression language in order of precedence from highest to lowest. Operators in | |
the same category have equal precedence. In the table, <span | |
class=CodeFragment>x</span>, <span class=CodeFragment>y</span>, and <span | |
class=CodeFragment>z</span> denote expressions, <span class=CodeFragment>T</span> | |
denotes a <a href="#_Types">type</a>, and <span class=CodeFragment>m</span> | |
denotes a member.</p> | |
<table class=MsoNormalTable border=0 cellspacing=0 cellpadding=0 | |
style='margin-left:.25in;border-collapse:collapse;mso-yfti-tbllook:1184; | |
mso-padding-alt:0in 0in 0in 0in'> | |
<thead> | |
<tr style='mso-yfti-irow:0;mso-yfti-firstrow:yes'> | |
<td width=103 valign=top style='width:77.4pt;border:solid black 1.0pt; | |
background:#D9D9D9;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><b>Category</b></p> | |
</td> | |
<td width=108 valign=top style='width:81.0pt;border:solid black 1.0pt; | |
border-left:none;background:#D9D9D9;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><b>Expression</b></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border:solid black 1.0pt; | |
border-left:none;background:#D9D9D9;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><b>Description</b></p> | |
</td> | |
</tr> | |
</thead> | |
<tr style='mso-yfti-irow:1'> | |
<td width=103 rowspan=10 valign=top style='width:77.4pt;border:solid black 1.0pt; | |
border-top:none;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Primary</p> | |
</td> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=SpellE><span | |
class=CodeFragment>x.m</span></span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Instance field or instance property | |
access. Any public field or property can be accessed.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:2'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=SpellE><span | |
class=GramE><span class=CodeFragment>x.m</span></span></span><span | |
class=GramE><span class=CodeFragment>(</span></span><span class=CodeFragment>…)</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Instance <a | |
href="#_Constructor_and_Method">method invocation</a>. The method must be | |
public and must be declared in an <a href="#_Predefined_types">accessible | |
type</a>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:3'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=GramE><span | |
class=CodeFragment>x[</span></span><span class=CodeFragment>…]</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Array or indexer access. | |
Multi-dimensional arrays are not supported.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:4'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=SpellE><span | |
class=CodeFragment>T.m</span></span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Static field or static property | |
access. Any public field or property can be accessed.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:5'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=SpellE><span | |
class=GramE><span class=CodeFragment>T.m</span></span></span><span | |
class=GramE><span class=CodeFragment>(</span></span><span class=CodeFragment>…)</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Static <a | |
href="#_Anonymous_Object_Creation">method invocation</a>. The method must be | |
public and must be declared in an <a href="#_Predefined_types">accessible | |
type</a>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:6'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=GramE><span | |
class=CodeFragment>T(</span></span><span class=CodeFragment>…)</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><a href="#_Conversions">Explicit | |
conversion</a> or <a href="#_Anonymous_Object_Creation">constructor | |
invocation</a>. Note that <span class=CodeFragment>new</span> is not required | |
in front of a constructor invocation.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:7'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=GramE><span | |
class=CodeFragment>new(</span></span><span class=CodeFragment>…)</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><a href="#_Data_Object_Initializer">Data | |
object <span class=SpellE>initializer</span></a>. This construct can be used | |
to perform dynamic projections.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:8'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>it</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><a href="#_Current_Instance">Current | |
instance</a>. In contexts where members of a current object are implicitly in | |
scope, <span class=CodeFragment>it</span> is used to refer to the entire | |
object itself.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:9'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=GramE><span | |
class=CodeFragment>x(</span></span><span class=CodeFragment>…)</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><a | |
href="#_Dynamic_Expression_Invocation">Dynamic lambda invocation</a>. Used to | |
reference another dynamic lambda expression.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:10'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=SpellE><span | |
class=CodeFragment>iif</span></span><span class=CodeFragment>(x,</span> <span | |
class=CodeFragment>y,</span> <span class=CodeFragment>z)</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Conditional expression. Alternate | |
syntax for <span class=GramE><span class=CodeFragment>x</span> <span | |
class=CodeFragment>?</span></span> <span class=CodeFragment>y</span> <span | |
class=CodeFragment>:</span> <span class=CodeFragment>z</span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:11'> | |
<td width=103 rowspan=2 valign=top style='width:77.4pt;border:solid black 1.0pt; | |
border-top:none;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Unary</p> | |
</td> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>-x</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Negation. Supported types are <span | |
class=CodeFragment>Int32</span>, <span class=CodeFragment>Int64</span>, <span | |
class=CodeFragment>Decimal</span>, <span class=CodeFragment>Single</span>, | |
and <span class=CodeFragment>Double</span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:12'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>!x</span></p> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>not x</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Logical negation. Operand must be <span | |
class=GramE>of</span> type <span class=CodeFragment>Boolean</span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:13'> | |
<td width=103 rowspan=3 valign=top style='width:77.4pt;border:solid black 1.0pt; | |
border-top:none;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Multiplicative</p> | |
</td> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x * y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Multiplication. Supported types are | |
<span class=CodeFragment>Int32</span>, <span class=CodeFragment>UInt32</span>, | |
<span class=CodeFragment>Int64</span>, <span class=CodeFragment>UInt64</span>, | |
<span class=CodeFragment>Decimal</span>, <span class=CodeFragment>Single</span>, | |
and <span class=CodeFragment>Double</span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:14'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x / y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Division. Supported types are <span | |
class=CodeFragment>Int32</span>, <span class=CodeFragment>UInt32</span>, <span | |
class=CodeFragment>Int64</span>, <span class=CodeFragment>UInt64</span>, <span | |
class=CodeFragment>Decimal</span>, <span class=CodeFragment>Single</span>, | |
and <span class=CodeFragment>Double</span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:15'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x % y</span></p> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x mod y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Remainder. Supported types are <span | |
class=CodeFragment>Int32</span>, <span class=CodeFragment>UInt32</span>, <span | |
class=CodeFragment>Int64</span>, <span class=CodeFragment>UInt64</span>, <span | |
class=CodeFragment>Decimal</span>, <span class=CodeFragment>Single</span>, | |
and <span class=CodeFragment>Double</span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:16'> | |
<td width=103 rowspan=3 valign=top style='width:77.4pt;border:solid black 1.0pt; | |
border-top:none;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Additive</p> | |
</td> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x + y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Addition or string concatenation. | |
Performs string concatenation if either operand is of type <span | |
class=CodeFragment>String</span>. Otherwise, performs addition for the | |
supported types <span class=CodeFragment>Int32</span>, <span | |
class=CodeFragment>UInt32</span>, <span class=CodeFragment>Int64</span>, <span | |
class=CodeFragment>UInt64</span>, <span class=CodeFragment>Decimal</span>, <span | |
class=CodeFragment>Single</span>, <span class=CodeFragment>Double</span>, <span | |
class=SpellE><span class=CodeFragment>DateTime</span></span>, and <span | |
class=SpellE><span class=CodeFragment>TimeSpan</span></span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:17'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x – y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Subtraction. Supported types are <span | |
class=CodeFragment>Int32</span>, <span class=CodeFragment>UInt32</span>, <span | |
class=CodeFragment>Int64</span>, <span class=CodeFragment>UInt64</span>, <span | |
class=GramE><span class=CodeFragment>Decimal</span></span>, <span | |
class=CodeFragment>Single</span>, <span class=CodeFragment>Double</span>, <span | |
class=SpellE><span class=CodeFragment>DateTime</span></span>, and <span | |
class=SpellE><span class=CodeFragment>TimeSpan</span></span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:18'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x & y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>String concatenation. Operands may | |
be of any type.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:19'> | |
<td width=103 rowspan=6 valign=top style='width:77.4pt;border:solid black 1.0pt; | |
border-top:none;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Relational</p> | |
</td> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x = y</span></p> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x == y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Equal. Supported for reference | |
types and the <a href="#_Predefined_types">primitive types</a>. Assignment is | |
not supported.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:20'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x != y</span></p> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x <> | |
y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Not equal. Supported for reference | |
types and the <a href="#_Predefined_types">primitive types</a>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:21'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x < y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Less than. Supported for all <a | |
href="#_Predefined_types">primitive types</a> except <span | |
class=CodeFragment>Boolean</span>, <span class=CodeFragment>Object</span> and | |
<span class=SpellE><span class=CodeFragment>Guid</span></span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:22'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x > y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Greater than. Supported for all <a | |
href="#_Predefined_types">primitive types</a> except <span | |
class=CodeFragment>Boolean</span>, <span class=CodeFragment>Object</span> and | |
<span class=SpellE><span class=CodeFragment>Guid</span></span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:23'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x <= y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Less than or equal. Supported for | |
all <a href="#_Predefined_types">primitive types</a> except <span | |
class=CodeFragment>Boolean</span>, <span class=CodeFragment>Object</span> and | |
<span class=SpellE><span class=CodeFragment>Guid</span></span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:24'> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x >= y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Greater than or equal. Supported | |
for all <a href="#_Predefined_types">primitive types</a> except <span | |
class=CodeFragment>Boolean</span>, <span class=CodeFragment>Object</span> and | |
<span class=SpellE><span class=CodeFragment>Guid</span></span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:25'> | |
<td width=103 valign=top style='width:77.4pt;border:solid black 1.0pt; | |
border-top:none;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Logical AND</p> | |
</td> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x | |
&& y</span></p> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x and y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Logical AND. Operands must be <span | |
class=GramE>of</span> type <span class=CodeFragment>Boolean</span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:26'> | |
<td width=103 valign=top style='width:77.4pt;border:solid black 1.0pt; | |
border-top:none;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Logical OR</p> | |
</td> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x || y</span></p> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=CodeFragment>x or y</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Logical OR. Operands must be <span | |
class=GramE>of</span> type <span class=CodeFragment>Boolean</span>.</p> | |
</td> | |
</tr> | |
<tr style='mso-yfti-irow:27;mso-yfti-lastrow:yes'> | |
<td width=103 valign=top style='width:77.4pt;border:solid black 1.0pt; | |
border-top:none;padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Conditional</p> | |
</td> | |
<td width=108 valign=top style='width:81.0pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'><span class=GramE><span | |
class=CodeFragment>x ?</span></span><span class=CodeFragment> y : z</span></p> | |
</td> | |
<td width=354 valign=top style='width:265.5pt;border-top:none;border-left: | |
none;border-bottom:solid black 1.0pt;border-right:solid black 1.0pt; | |
padding:0in 5.4pt 0in 5.4pt'> | |
<p class=MsoNormal style='margin-top:3.0pt;margin-right:0in;margin-bottom: | |
3.0pt;margin-left:0in;line-height:normal'>Evaluates <span class=CodeFragment>y</span> | |
if <span class=CodeFragment>x</span> is true, evaluates <span | |
class=CodeFragment>z</span> if <span class=CodeFragment>x</span> is false.</p> | |
</td> | |
</tr> | |
</table> | |
<h2><a name="_Anonymous_Object_Creation"></a><a name="_Constructor_and_Method"></a><span | |
style='mso-fareast-font-family:"Times New Roman"'>Method and Constructor | |
Invocations<o:p></o:p></span></h2> | |
<p class=MsoNormal>The expression language limits invocation of methods and | |
constructors to those declared public in the <a href="#_Predefined_types">accessible | |
types</a>. This restriction exists to protect against unintended side effects | |
from invocation of arbitrary methods.</p> | |
<p class=MsoNormal>The expression language permits getting (but not setting) | |
the value of any reachable public field, property, or indexer.</p> | |
<p class=MsoNormal>Overload resolution for methods, constructors, and indexers | |
uses rules similar to C#. In informal terms, overload resolution will pick the | |
best matching method, constructor, or indexer, or report an ambiguity error if | |
no single best match can be identified.</p> | |
<p class=MsoNormal>Note that constructor invocations are not prefixed by <span | |
class=CodeFragment>new</span>. The following example creates a <span | |
class=SpellE><span class=CodeFragment>DateTime</span></span> instance for a <span | |
class=SpellE>specfic</span> year, month, and day using a constructor | |
invocation:</p> | |
<p class=Code><span class=SpellE><span class=GramE>orders.Where</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"<span class=SpellE>OrderDate</span> | |
>= <span class=SpellE>DateTime</span>(2007, 1, 1)"</span>);</p> | |
<h2><a name="_Anonymous_Object_Initializer"></a><a | |
name="_Data_Object_Initializer"></a><span style='mso-fareast-font-family:"Times New Roman"'>Data | |
Object Initializers<o:p></o:p></span></h2> | |
<p class=MsoNormal>A data object <span class=SpellE>initializer</span> creates | |
a <a href="#_Dynamic_Data_Classes">data class</a> and returns an instance of | |
that class. The properties of the data class are inferred from the data object <span | |
class=SpellE>initializer</span>. Specifically, a data object <span | |
class=SpellE>initializer</span> of the form</p> | |
<p class=Code><span class=GramE>new(</span>e1 as p1, e2 as p2, e3 as p3)</p> | |
<p class=MsoNormal><span class=GramE>creates</span> a data class with three | |
properties, <span class=CodeFragment>p1</span>, <span class=CodeFragment>p2</span>, | |
and <span class=CodeFragment>p3</span>, the types of which are inferred from | |
the expressions <span class=CodeFragment>e1</span>, <span class=CodeFragment>e2</span>, | |
and <span class=CodeFragment>e3</span>, and returns an instance of that data | |
class with the properties initialized to the values computed by <span | |
class=CodeFragment>e1</span>, <span class=CodeFragment>e2</span>, and <span | |
class=CodeFragment>e3</span>. A property <span class=SpellE>initializer</span> | |
may omit the <span class=CodeFragment>as</span> keyword and the property name | |
provided the associated expression is a field or property access. The example</p> | |
<p class=Code><span class=SpellE><span class=GramE>customers.Select</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"new(<span class=SpellE>CompanyName</span> | |
as Name, Phone)"</span>);</p> | |
<p class=MsoNormal><span class=GramE>creates</span> a data class with two | |
properties, <span class=CodeFragment>Name</span> and <span class=CodeFragment>Phone</span>, | |
and returns a sequence of instances of that data class initialized from the <span | |
class=SpellE><span class=CodeFragment>CompanyName</span></span> and <span | |
class=CodeFragment>Phone</span> properties of each customer.</p> | |
<h2><a name="_Current_Instance"></a><span style='mso-fareast-font-family:"Times New Roman"'>Current | |
Instance<o:p></o:p></span></h2> | |
<p class=MsoNormal>When parsing a lambda expression with a single unnamed | |
parameter, the members of the unnamed parameter are automatically in scope in | |
the expression string, and the <a href="#_Current_Instance"><em><span | |
style='font-family:"Calibri","sans-serif";color:windowtext;text-decoration: | |
none;text-underline:none'>current instance</span></em></a> given by the unnamed | |
parameter can be referenced in whole using the keyword <span | |
class=CodeFragment>it</span>. For example,</p> | |
<p class=Code><span class=SpellE><span class=GramE>customers.Where</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"Country = @0"</span>, | |
country);</p> | |
<p class=MsoNormal><span class=GramE>is</span> equivalent to</p> | |
<p class=Code><span class=SpellE><span class=GramE>customers.Where</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"<span class=SpellE>it.Country</span> | |
= @0"</span>, country);</p> | |
<p class=MsoNormal>The <a href="#_IQueryable_Extension_Methods">IQueryable | |
extension methods</a> all parse their expression arguments as lambda | |
expressions with a single unnamed parameter.</p> | |
<h2><a name="_Dynamic_Expression_Invocation"></a><span style='mso-fareast-font-family: | |
"Times New Roman"'>Dynamic Lambda Invocation<o:p></o:p></span></h2> | |
<p class=MsoNormal>An expression can reference other dynamic lambda expressions | |
through <em><span style='font-family:"Calibri","sans-serif"'>dynamic lambda | |
invocations</span></em>. A dynamic lambda invocation consists of a substitution | |
variable identifier that references an instance of <span class=SpellE><span | |
class=CodeFragment>System.Linq.Expressions.LambdaExpression</span></span>, | |
followed by an argument list. The arguments supplied must be compatible with | |
the parameter list of the given dynamic lambda expression.</p> | |
<p class=MsoNormal>The following parses two separate dynamic lambda expressions | |
and then combines them in a predicate expression through dynamic lambda | |
invocations:</p> | |
<p class=Code><span style='color:#2B91AF'>Expression</span><<span | |
class=SpellE><span style='color:#2B91AF'>Func</span></span><<span | |
style='color:#2B91AF'>Customer</span>, <span class=SpellE><span | |
style='color:blue'>bool</span></span>>> e1 = <br> | |
<span class=SpellE><span style='color:#2B91AF'>DynamicExpression</span>.ParseLambda</span><<span | |
style='color:#2B91AF'>Customer</span>, <span class=SpellE><span | |
style='color:blue'>bool</span></span><span class=GramE>>(</span><span | |
style='color:#A31515'>"City = \"London\""</span>);<br> | |
<span style='color:#2B91AF'>Expression</span><<span class=SpellE><span | |
style='color:#2B91AF'>Func</span></span><<span style='color:#2B91AF'>Customer</span>, | |
<span class=SpellE><span style='color:blue'>bool</span></span>>> e2 =<br> | |
<span class=SpellE><span style='color:#2B91AF'>DynamicExpression</span>.ParseLambda</span><<span | |
style='color:#2B91AF'>Customer</span>, <span class=SpellE><span | |
style='color:blue'>bool</span></span>>(<span style='color:#A31515'>"<span | |
class=SpellE>Orders.Count</span> >= 10"</span>);<br> | |
<span style='color:#2B91AF'>IQueryable</span><<span style='color:#2B91AF'>Customer</span>> | |
query =<br> | |
<span class=SpellE>db.Customers.Where</span>(<span | |
style='color:#A31515'>"@0(it) and @1(it)"</span>, e1, e2);</p> | |
<p class=MsoNormal>It is of course possible to combine static and dynamic | |
lambda expressions in this fashion:</p> | |
<p class=Code><span style='color:#2B91AF'>Expression</span><<span | |
class=SpellE><span style='color:#2B91AF'>Func</span></span><<span | |
style='color:#2B91AF'>Customer</span>, <span class=SpellE><span | |
style='color:blue'>bool</span></span>>> e1 =<br> | |
c => <span class=SpellE>c.City</span> == <span | |
style='color:#A31515'>"London"</span><span class=GramE>;</span><br> | |
<span style='color:#2B91AF'>Expression</span><<span class=SpellE><span | |
style='color:#2B91AF'>Func</span></span><<span style='color:#2B91AF'>Customer</span>, | |
<span class=SpellE><span style='color:blue'>bool</span></span>>> e2 =<br> | |
<span class=SpellE><span style='color:#2B91AF'>DynamicExpression</span>.ParseLambda</span><<span | |
style='color:#2B91AF'>Customer</span>, <span class=SpellE><span | |
style='color:blue'>bool</span></span>>(<span style='color:#A31515'>"<span | |
class=SpellE>Orders.Count</span> >= 10"</span>);<br> | |
<span style='color:#2B91AF'>IQueryable</span><<span style='color:#2B91AF'>Customer</span>> | |
query =<br> | |
<span class=SpellE>db.Customers.Where</span>(<span | |
style='color:#A31515'>"@0(it) and @1(it)"</span>, e1, e2);</p> | |
<p class=MsoNormal>The examples above both have the same effect as:</p> | |
<p class=Code><span style='color:#2B91AF'>IQueryable</span><<span | |
style='color:#2B91AF'>Customer</span>> query =<br> | |
<span class=SpellE>db.Customers.Where</span>(c => <span | |
class=SpellE>c.City</span> == <span style='color:#A31515'>"London"</span> | |
&& <span class=SpellE>c.Orders.Count</span> >= 10)<span class=GramE>;<span | |
style='display:none;mso-hide:all'>n</span></span><span style='display:none; | |
mso-hide:all'> a predicate expression c lambda expressions and then combines | |
them through dynamic lambda invocations</span></p> | |
<h2><span class=GramE><span style='mso-fareast-font-family:"Times New Roman"; | |
display:none;mso-hide:all'>combines</span></span><span style='mso-fareast-font-family: | |
"Times New Roman";display:none;mso-hide:all'> two <span class=SpellE>seperately</span> | |
parsed lambda expressions in a single <span class=SpellE>predicate:e</span> | |
dynamic lambda expression. <span class=SpellE>System.Linq.Expressions.Lam<span | |
style='mso-hide:none'>Sequence</span></span></span><span style='mso-fareast-font-family: | |
"Times New Roman"'> operators<o:p></o:p></span></h2> | |
<p class=MsoNormal>A subset of the Standard Query Operators is supported for | |
objects that implement <span class=SpellE><span class=CodeFragment>IEnumerable</span></span><span | |
class=CodeFragment><T></span>. Specifically, the following constructs are | |
permitted, where <span class=SpellE><i>seq</i></span> is an <span class=SpellE><span | |
class=CodeFragment>IEnumerable</span></span><span class=CodeFragment><T></span> | |
instance, <i>predicate</i> is a <span class=SpellE><span class=GramE>boolean</span></span> | |
expression, and <i>selector</i> is an expression of any type:</p> | |
<p class=MsoNormal style='margin-left:.5in'><span class=SpellE><span | |
class=GramE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span></span><span | |
class=GramE> <span class=CodeFragment>.</span></span> <span class=CodeFragment>Where</span> | |
<span class=GramE><span class=CodeFragment>(</span> <em><span style='font-family: | |
"Calibri","sans-serif"'>predicate</span></em></span> <span class=CodeFragment>)</span> | |
<span class=SpellE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span> | |
<span class=CodeFragment>.</span> <span class=CodeFragment>Any</span> <span | |
class=CodeFragment>(</span> <span class=CodeFragment>)</span></p> | |
<p class=MsoNormal style='margin-left:.5in'><span class=SpellE><span | |
class=GramE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span></span><span | |
class=GramE> <span class=CodeFragment>.</span></span> <span class=CodeFragment>Any</span> | |
<span class=GramE><span class=CodeFragment>(</span> <em><span style='font-family: | |
"Calibri","sans-serif"'>predicate</span></em></span> <span class=CodeFragment>)</span> | |
<span class=SpellE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span> | |
<span class=CodeFragment>.</span> <span class=CodeFragment>All</span> <span | |
class=GramE><span class=CodeFragment>(</span> <em><span style='font-family: | |
"Calibri","sans-serif"'>predicate</span></em></span> )</p> | |
<p class=MsoNormal style='margin-left:.5in'><span class=SpellE><span | |
class=GramE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span></span><span | |
class=GramE> <span class=CodeFragment>.</span></span> <span class=CodeFragment>Count</span> | |
<span class=CodeFragment>(</span> <span class=CodeFragment>)</span> | |
<span class=SpellE><span class=GramE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span></span><span | |
class=GramE> <span class=CodeFragment>.</span></span> <span class=CodeFragment>Count</span> | |
<span class=GramE><span class=CodeFragment>(</span> <em><span style='font-family: | |
"Calibri","sans-serif"'>predicate</span></em></span> <span class=CodeFragment>)</span></p> | |
<p class=MsoNormal style='margin-left:.5in'><span class=SpellE><span | |
class=GramE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span></span><span | |
class=GramE> <span class=CodeFragment>.</span></span> <span class=CodeFragment>Min</span> | |
<span class=GramE><span class=CodeFragment>(</span> <em><span style='font-family: | |
"Calibri","sans-serif"'>selector</span></em></span> <span class=CodeFragment>)</span> | |
<span class=SpellE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span> | |
<span class=CodeFragment>.</span> <span class=CodeFragment>Max</span> <span | |
class=GramE><span class=CodeFragment>(</span> <em><span style='font-family: | |
"Calibri","sans-serif"'>selector</span></em></span> <span class=CodeFragment>)</span></p> | |
<p class=MsoNormal style='margin-left:.5in'><span class=SpellE><span | |
class=GramE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span></span><span | |
class=GramE> <span class=CodeFragment>.</span></span> <span class=CodeFragment>Sum</span> | |
<span class=GramE><span class=CodeFragment>(</span> <em><span style='font-family: | |
"Calibri","sans-serif"'>selector</span></em></span> <span class=CodeFragment>)</span> | |
<span class=SpellE><em><span style='font-family:"Calibri","sans-serif"'>seq</span></em></span> | |
<span class=CodeFragment>.</span> <span class=CodeFragment>Average</span> <span | |
class=GramE><span class=CodeFragment>(</span> <em><span style='font-family: | |
"Calibri","sans-serif"'>selector</span></em></span> <span class=CodeFragment>)</span></p> | |
<p class=MsoNormal>In the <i>predicate</i> and <i>selector</i> expressions, the | |
members of the <a href="#_Current_Instance">current instance</a> for that | |
sequence operator are automatically in scope, and the instance itself can be | |
referenced using the keyword <span class=CodeFragment>it</span>. An example:</p> | |
<p class=Code><span class=SpellE><span class=GramE>customers.Where</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"<span class=SpellE>Orders.Any</span>(Total | |
>= 1000)"</span>);</p> | |
<h2><span class=SpellE><span style='mso-fareast-font-family:"Times New Roman"'>Enum</span></span><span | |
style='mso-fareast-font-family:"Times New Roman"'> type support<o:p></o:p></span></h2> | |
<p class=MsoNormal>The expression language supports an <a href="#_Conversions_1">implicit | |
conversion</a> from a string literal to an <span class=SpellE>enum</span> type | |
provided the string literal contains the name of a member of that <span | |
class=SpellE>enum</span> type. For example,</p> | |
<p class=Code><span class=SpellE><span class=GramE>orders.Where</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"<span class=SpellE>OrderDate.DayOfWeek</span> | |
= \"Monday\""</span>);</p> | |
<p class=MsoNormal><span class=GramE>is</span> equivalent to</p> | |
<p class=Code><span class=SpellE><span class=GramE>orders.Where</span></span><span | |
class=GramE>(</span><span style='color:#A31515'>"<span class=SpellE>OrderDate.DayOfWeek</span> | |
= @0"</span>, <span class=SpellE><span style='color:#2B91AF'>DayOfWeek</span>.Monday</span>);</p> | |
<p class=MsoNormal> </p> | |
</div> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Copyright (C) Microsoft Corporation. All rights reserved. | |
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using System.Reflection.Emit; | |
using System.Threading; | |
namespace System.Linq.Dynamic | |
{ | |
public static class DynamicQueryable | |
{ | |
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values) { | |
return (IQueryable<T>)Where((IQueryable)source, predicate, values); | |
} | |
public static IQueryable Where(this IQueryable source, string predicate, params object[] values) { | |
if (source == null) throw new ArgumentNullException("source"); | |
if (predicate == null) throw new ArgumentNullException("predicate"); | |
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values); | |
return source.Provider.CreateQuery( | |
Expression.Call( | |
typeof(Queryable), "Where", | |
new Type[] { source.ElementType }, | |
source.Expression, Expression.Quote(lambda))); | |
} | |
public static IQueryable Select(this IQueryable source, string selector, params object[] values) { | |
if (source == null) throw new ArgumentNullException("source"); | |
if (selector == null) throw new ArgumentNullException("selector"); | |
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values); | |
return source.Provider.CreateQuery( | |
Expression.Call( | |
typeof(Queryable), "Select", | |
new Type[] { source.ElementType, lambda.Body.Type }, | |
source.Expression, Expression.Quote(lambda))); | |
} | |
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) { | |
return (IQueryable<T>)OrderBy((IQueryable)source, ordering, values); | |
} | |
public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) { | |
if (source == null) throw new ArgumentNullException("source"); | |
if (ordering == null) throw new ArgumentNullException("ordering"); | |
ParameterExpression[] parameters = new ParameterExpression[] { | |
Expression.Parameter(source.ElementType, "") }; | |
ExpressionParser parser = new ExpressionParser(parameters, ordering, values); | |
IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering(); | |
Expression queryExpr = source.Expression; | |
string methodAsc = "OrderBy"; | |
string methodDesc = "OrderByDescending"; | |
foreach (DynamicOrdering o in orderings) { | |
queryExpr = Expression.Call( | |
typeof(Queryable), o.Ascending ? methodAsc : methodDesc, | |
new Type[] { source.ElementType, o.Selector.Type }, | |
queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters))); | |
methodAsc = "ThenBy"; | |
methodDesc = "ThenByDescending"; | |
} | |
return source.Provider.CreateQuery(queryExpr); | |
} | |
public static IQueryable Take(this IQueryable source, int count) { | |
if (source == null) throw new ArgumentNullException("source"); | |
return source.Provider.CreateQuery( | |
Expression.Call( | |
typeof(Queryable), "Take", | |
new Type[] { source.ElementType }, | |
source.Expression, Expression.Constant(count))); | |
} | |
public static IQueryable Skip(this IQueryable source, int count) { | |
if (source == null) throw new ArgumentNullException("source"); | |
return source.Provider.CreateQuery( | |
Expression.Call( | |
typeof(Queryable), "Skip", | |
new Type[] { source.ElementType }, | |
source.Expression, Expression.Constant(count))); | |
} | |
public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) { | |
if (source == null) throw new ArgumentNullException("source"); | |
if (keySelector == null) throw new ArgumentNullException("keySelector"); | |
if (elementSelector == null) throw new ArgumentNullException("elementSelector"); | |
LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values); | |
LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values); | |
return source.Provider.CreateQuery( | |
Expression.Call( | |
typeof(Queryable), "GroupBy", | |
new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type }, | |
source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda))); | |
} | |
public static bool Any(this IQueryable source) { | |
if (source == null) throw new ArgumentNullException("source"); | |
return (bool)source.Provider.Execute( | |
Expression.Call( | |
typeof(Queryable), "Any", | |
new Type[] { source.ElementType }, source.Expression)); | |
} | |
public static int Count(this IQueryable source) { | |
if (source == null) throw new ArgumentNullException("source"); | |
return (int)source.Provider.Execute( | |
Expression.Call( | |
typeof(Queryable), "Count", | |
new Type[] { source.ElementType }, source.Expression)); | |
} | |
} | |
public abstract class DynamicClass | |
{ | |
public override string ToString() { | |
PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); | |
StringBuilder sb = new StringBuilder(); | |
sb.Append("{"); | |
for (int i = 0; i < props.Length; i++) { | |
if (i > 0) sb.Append(", "); | |
sb.Append(props[i].Name); | |
sb.Append("="); | |
sb.Append(props[i].GetValue(this, null)); | |
} | |
sb.Append("}"); | |
return sb.ToString(); | |
} | |
} | |
public class DynamicProperty | |
{ | |
string name; | |
Type type; | |
public DynamicProperty(string name, Type type) { | |
if (name == null) throw new ArgumentNullException("name"); | |
if (type == null) throw new ArgumentNullException("type"); | |
this.name = name; | |
this.type = type; | |
} | |
public string Name { | |
get { return name; } | |
} | |
public Type Type { | |
get { return type; } | |
} | |
} | |
public static class DynamicExpression | |
{ | |
public static Expression Parse(Type resultType, string expression, params object[] values) { | |
ExpressionParser parser = new ExpressionParser(null, expression, values); | |
return parser.Parse(resultType); | |
} | |
public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, params object[] values) { | |
return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, values); | |
} | |
public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) { | |
ExpressionParser parser = new ExpressionParser(parameters, expression, values); | |
return Expression.Lambda(parser.Parse(resultType), parameters); | |
} | |
public static Expression<Func<T, S>> ParseLambda<T, S>(string expression, params object[] values) { | |
return (Expression<Func<T, S>>)ParseLambda(typeof(T), typeof(S), expression, values); | |
} | |
public static Type CreateClass(params DynamicProperty[] properties) { | |
return ClassFactory.Instance.GetDynamicClass(properties); | |
} | |
public static Type CreateClass(IEnumerable<DynamicProperty> properties) { | |
return ClassFactory.Instance.GetDynamicClass(properties); | |
} | |
} | |
internal class DynamicOrdering | |
{ | |
public Expression Selector; | |
public bool Ascending; | |
} | |
internal class Signature : IEquatable<Signature> | |
{ | |
public DynamicProperty[] properties; | |
public int hashCode; | |
public Signature(IEnumerable<DynamicProperty> properties) { | |
this.properties = properties.ToArray(); | |
hashCode = 0; | |
foreach (DynamicProperty p in properties) { | |
hashCode ^= p.Name.GetHashCode() ^ p.Type.GetHashCode(); | |
} | |
} | |
public override int GetHashCode() { | |
return hashCode; | |
} | |
public override bool Equals(object obj) { | |
return obj is Signature ? Equals((Signature)obj) : false; | |
} | |
public bool Equals(Signature other) { | |
if (properties.Length != other.properties.Length) return false; | |
for (int i = 0; i < properties.Length; i++) { | |
if (properties[i].Name != other.properties[i].Name || | |
properties[i].Type != other.properties[i].Type) return false; | |
} | |
return true; | |
} | |
} | |
internal class ClassFactory | |
{ | |
public static readonly ClassFactory Instance = new ClassFactory(); | |
static ClassFactory() { } // Trigger lazy initialization of static fields | |
ModuleBuilder module; | |
Dictionary<Signature, Type> classes; | |
int classCount; | |
ReaderWriterLock rwLock; | |
private ClassFactory() { | |
AssemblyName name = new AssemblyName("DynamicClasses"); | |
AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); | |
#if ENABLE_LINQ_PARTIAL_TRUST | |
new ReflectionPermission(PermissionState.Unrestricted).Assert(); | |
#endif | |
try { | |
module = assembly.DefineDynamicModule("Module"); | |
} | |
finally { | |
#if ENABLE_LINQ_PARTIAL_TRUST | |
PermissionSet.RevertAssert(); | |
#endif | |
} | |
classes = new Dictionary<Signature, Type>(); | |
rwLock = new ReaderWriterLock(); | |
} | |
public Type GetDynamicClass(IEnumerable<DynamicProperty> properties) { | |
rwLock.AcquireReaderLock(Timeout.Infinite); | |
try { | |
Signature signature = new Signature(properties); | |
Type type; | |
if (!classes.TryGetValue(signature, out type)) { | |
type = CreateDynamicClass(signature.properties); | |
classes.Add(signature, type); | |
} | |
return type; | |
} | |
finally { | |
rwLock.ReleaseReaderLock(); | |
} | |
} | |
Type CreateDynamicClass(DynamicProperty[] properties) { | |
LockCookie cookie = rwLock.UpgradeToWriterLock(Timeout.Infinite); | |
try { | |
string typeName = "DynamicClass" + (classCount + 1); | |
#if ENABLE_LINQ_PARTIAL_TRUST | |
new ReflectionPermission(PermissionState.Unrestricted).Assert(); | |
#endif | |
try { | |
TypeBuilder tb = this.module.DefineType(typeName, TypeAttributes.Class | | |
TypeAttributes.Public, typeof(DynamicClass)); | |
FieldInfo[] fields = GenerateProperties(tb, properties); | |
GenerateEquals(tb, fields); | |
GenerateGetHashCode(tb, fields); | |
Type result = tb.CreateType(); | |
classCount++; | |
return result; | |
} | |
finally { | |
#if ENABLE_LINQ_PARTIAL_TRUST | |
PermissionSet.RevertAssert(); | |
#endif | |
} | |
} | |
finally { | |
rwLock.DowngradeFromWriterLock(ref cookie); | |
} | |
} | |
FieldInfo[] GenerateProperties(TypeBuilder tb, DynamicProperty[] properties) { | |
FieldInfo[] fields = new FieldBuilder[properties.Length]; | |
for (int i = 0; i < properties.Length; i++) { | |
DynamicProperty dp = properties[i]; | |
FieldBuilder fb = tb.DefineField("_" + dp.Name, dp.Type, FieldAttributes.Private); | |
PropertyBuilder pb = tb.DefineProperty(dp.Name, PropertyAttributes.HasDefault, dp.Type, null); | |
MethodBuilder mbGet = tb.DefineMethod("get_" + dp.Name, | |
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, | |
dp.Type, Type.EmptyTypes); | |
ILGenerator genGet = mbGet.GetILGenerator(); | |
genGet.Emit(OpCodes.Ldarg_0); | |
genGet.Emit(OpCodes.Ldfld, fb); | |
genGet.Emit(OpCodes.Ret); | |
MethodBuilder mbSet = tb.DefineMethod("set_" + dp.Name, | |
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, | |
null, new Type[] { dp.Type }); | |
ILGenerator genSet = mbSet.GetILGenerator(); | |
genSet.Emit(OpCodes.Ldarg_0); | |
genSet.Emit(OpCodes.Ldarg_1); | |
genSet.Emit(OpCodes.Stfld, fb); | |
genSet.Emit(OpCodes.Ret); | |
pb.SetGetMethod(mbGet); | |
pb.SetSetMethod(mbSet); | |
fields[i] = fb; | |
} | |
return fields; | |
} | |
void GenerateEquals(TypeBuilder tb, FieldInfo[] fields) { | |
MethodBuilder mb = tb.DefineMethod("Equals", | |
MethodAttributes.Public | MethodAttributes.ReuseSlot | | |
MethodAttributes.Virtual | MethodAttributes.HideBySig, | |
typeof(bool), new Type[] { typeof(object) }); | |
ILGenerator gen = mb.GetILGenerator(); | |
LocalBuilder other = gen.DeclareLocal(tb); | |
Label next = gen.DefineLabel(); | |
gen.Emit(OpCodes.Ldarg_1); | |
gen.Emit(OpCodes.Isinst, tb); | |
gen.Emit(OpCodes.Stloc, other); | |
gen.Emit(OpCodes.Ldloc, other); | |
gen.Emit(OpCodes.Brtrue_S, next); | |
gen.Emit(OpCodes.Ldc_I4_0); | |
gen.Emit(OpCodes.Ret); | |
gen.MarkLabel(next); | |
foreach (FieldInfo field in fields) { | |
Type ft = field.FieldType; | |
Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); | |
next = gen.DefineLabel(); | |
gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); | |
gen.Emit(OpCodes.Ldarg_0); | |
gen.Emit(OpCodes.Ldfld, field); | |
gen.Emit(OpCodes.Ldloc, other); | |
gen.Emit(OpCodes.Ldfld, field); | |
gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("Equals", new Type[] { ft, ft }), null); | |
gen.Emit(OpCodes.Brtrue_S, next); | |
gen.Emit(OpCodes.Ldc_I4_0); | |
gen.Emit(OpCodes.Ret); | |
gen.MarkLabel(next); | |
} | |
gen.Emit(OpCodes.Ldc_I4_1); | |
gen.Emit(OpCodes.Ret); | |
} | |
void GenerateGetHashCode(TypeBuilder tb, FieldInfo[] fields) { | |
MethodBuilder mb = tb.DefineMethod("GetHashCode", | |
MethodAttributes.Public | MethodAttributes.ReuseSlot | | |
MethodAttributes.Virtual | MethodAttributes.HideBySig, | |
typeof(int), Type.EmptyTypes); | |
ILGenerator gen = mb.GetILGenerator(); | |
gen.Emit(OpCodes.Ldc_I4_0); | |
foreach (FieldInfo field in fields) { | |
Type ft = field.FieldType; | |
Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); | |
gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); | |
gen.Emit(OpCodes.Ldarg_0); | |
gen.Emit(OpCodes.Ldfld, field); | |
gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("GetHashCode", new Type[] { ft }), null); | |
gen.Emit(OpCodes.Xor); | |
} | |
gen.Emit(OpCodes.Ret); | |
} | |
} | |
public sealed class ParseException : Exception | |
{ | |
int position; | |
public ParseException(string message, int position) | |
: base(message) { | |
this.position = position; | |
} | |
public int Position { | |
get { return position; } | |
} | |
public override string ToString() { | |
return string.Format(Res.ParseExceptionFormat, Message, position); | |
} | |
} | |
internal class ExpressionParser | |
{ | |
struct Token | |
{ | |
public TokenId id; | |
public string text; | |
public int pos; | |
} | |
enum TokenId | |
{ | |
Unknown, | |
End, | |
Identifier, | |
StringLiteral, | |
IntegerLiteral, | |
RealLiteral, | |
Exclamation, | |
Percent, | |
Amphersand, | |
OpenParen, | |
CloseParen, | |
Asterisk, | |
Plus, | |
Comma, | |
Minus, | |
Dot, | |
Slash, | |
Colon, | |
LessThan, | |
Equal, | |
GreaterThan, | |
Question, | |
OpenBracket, | |
CloseBracket, | |
Bar, | |
ExclamationEqual, | |
DoubleAmphersand, | |
LessThanEqual, | |
LessGreater, | |
DoubleEqual, | |
GreaterThanEqual, | |
DoubleBar | |
} | |
interface ILogicalSignatures | |
{ | |
void F(bool x, bool y); | |
void F(bool? x, bool? y); | |
} | |
interface IArithmeticSignatures | |
{ | |
void F(int x, int y); | |
void F(uint x, uint y); | |
void F(long x, long y); | |
void F(ulong x, ulong y); | |
void F(float x, float y); | |
void F(double x, double y); | |
void F(decimal x, decimal y); | |
void F(int? x, int? y); | |
void F(uint? x, uint? y); | |
void F(long? x, long? y); | |
void F(ulong? x, ulong? y); | |
void F(float? x, float? y); | |
void F(double? x, double? y); | |
void F(decimal? x, decimal? y); | |
} | |
interface IRelationalSignatures : IArithmeticSignatures | |
{ | |
void F(string x, string y); | |
void F(char x, char y); | |
void F(DateTime x, DateTime y); | |
void F(TimeSpan x, TimeSpan y); | |
void F(char? x, char? y); | |
void F(DateTime? x, DateTime? y); | |
void F(TimeSpan? x, TimeSpan? y); | |
} | |
interface IEqualitySignatures : IRelationalSignatures | |
{ | |
void F(bool x, bool y); | |
void F(bool? x, bool? y); | |
} | |
interface IAddSignatures : IArithmeticSignatures | |
{ | |
void F(DateTime x, TimeSpan y); | |
void F(TimeSpan x, TimeSpan y); | |
void F(DateTime? x, TimeSpan? y); | |
void F(TimeSpan? x, TimeSpan? y); | |
} | |
interface ISubtractSignatures : IAddSignatures | |
{ | |
void F(DateTime x, DateTime y); | |
void F(DateTime? x, DateTime? y); | |
} | |
interface INegationSignatures | |
{ | |
void F(int x); | |
void F(long x); | |
void F(float x); | |
void F(double x); | |
void F(decimal x); | |
void F(int? x); | |
void F(long? x); | |
void F(float? x); | |
void F(double? x); | |
void F(decimal? x); | |
} | |
interface INotSignatures | |
{ | |
void F(bool x); | |
void F(bool? x); | |
} | |
interface IEnumerableSignatures | |
{ | |
void Where(bool predicate); | |
void Any(); | |
void Any(bool predicate); | |
void All(bool predicate); | |
void Count(); | |
void Count(bool predicate); | |
void Min(object selector); | |
void Max(object selector); | |
void Sum(int selector); | |
void Sum(int? selector); | |
void Sum(long selector); | |
void Sum(long? selector); | |
void Sum(float selector); | |
void Sum(float? selector); | |
void Sum(double selector); | |
void Sum(double? selector); | |
void Sum(decimal selector); | |
void Sum(decimal? selector); | |
void Average(int selector); | |
void Average(int? selector); | |
void Average(long selector); | |
void Average(long? selector); | |
void Average(float selector); | |
void Average(float? selector); | |
void Average(double selector); | |
void Average(double? selector); | |
void Average(decimal selector); | |
void Average(decimal? selector); | |
} | |
static readonly Type[] predefinedTypes = { | |
typeof(Object), | |
typeof(Boolean), | |
typeof(Char), | |
typeof(String), | |
typeof(SByte), | |
typeof(Byte), | |
typeof(Int16), | |
typeof(UInt16), | |
typeof(Int32), | |
typeof(UInt32), | |
typeof(Int64), | |
typeof(UInt64), | |
typeof(Single), | |
typeof(Double), | |
typeof(Decimal), | |
typeof(DateTime), | |
typeof(TimeSpan), | |
typeof(Guid), | |
typeof(Math), | |
typeof(Convert) | |
}; | |
static readonly Expression trueLiteral = Expression.Constant(true); | |
static readonly Expression falseLiteral = Expression.Constant(false); | |
static readonly Expression nullLiteral = Expression.Constant(null); | |
static readonly string keywordIt = "it"; | |
static readonly string keywordIif = "iif"; | |
static readonly string keywordNew = "new"; | |
static Dictionary<string, object> keywords; | |
Dictionary<string, object> symbols; | |
IDictionary<string, object> externals; | |
Dictionary<Expression, string> literals; | |
ParameterExpression it; | |
string text; | |
int textPos; | |
int textLen; | |
char ch; | |
Token token; | |
public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) { | |
if (expression == null) throw new ArgumentNullException("expression"); | |
if (keywords == null) keywords = CreateKeywords(); | |
symbols = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); | |
literals = new Dictionary<Expression, string>(); | |
if (parameters != null) ProcessParameters(parameters); | |
if (values != null) ProcessValues(values); | |
text = expression; | |
textLen = text.Length; | |
SetTextPos(0); | |
NextToken(); | |
} | |
void ProcessParameters(ParameterExpression[] parameters) { | |
foreach (ParameterExpression pe in parameters) | |
if (!String.IsNullOrEmpty(pe.Name)) | |
AddSymbol(pe.Name, pe); | |
if (parameters.Length == 1 && String.IsNullOrEmpty(parameters[0].Name)) | |
it = parameters[0]; | |
} | |
void ProcessValues(object[] values) { | |
for (int i = 0; i < values.Length; i++) { | |
object value = values[i]; | |
if (i == values.Length - 1 && value is IDictionary<string, object>) { | |
externals = (IDictionary<string, object>)value; | |
} | |
else { | |
AddSymbol("@" + i.ToString(System.Globalization.CultureInfo.InvariantCulture), value); | |
} | |
} | |
} | |
void AddSymbol(string name, object value) { | |
if (symbols.ContainsKey(name)) | |
throw ParseError(Res.DuplicateIdentifier, name); | |
symbols.Add(name, value); | |
} | |
public Expression Parse(Type resultType) { | |
int exprPos = token.pos; | |
Expression expr = ParseExpression(); | |
if (resultType != null) | |
if ((expr = PromoteExpression(expr, resultType, true)) == null) | |
throw ParseError(exprPos, Res.ExpressionTypeMismatch, GetTypeName(resultType)); | |
ValidateToken(TokenId.End, Res.SyntaxError); | |
return expr; | |
} | |
#pragma warning disable 0219 | |
public IEnumerable<DynamicOrdering> ParseOrdering() { | |
List<DynamicOrdering> orderings = new List<DynamicOrdering>(); | |
while (true) { | |
Expression expr = ParseExpression(); | |
bool ascending = true; | |
if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending")) { | |
NextToken(); | |
} | |
else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending")) { | |
NextToken(); | |
ascending = false; | |
} | |
orderings.Add(new DynamicOrdering { Selector = expr, Ascending = ascending }); | |
if (token.id != TokenId.Comma) break; | |
NextToken(); | |
} | |
ValidateToken(TokenId.End, Res.SyntaxError); | |
return orderings; | |
} | |
#pragma warning restore 0219 | |
// ?: operator | |
Expression ParseExpression() { | |
int errorPos = token.pos; | |
Expression expr = ParseLogicalOr(); | |
if (token.id == TokenId.Question) { | |
NextToken(); | |
Expression expr1 = ParseExpression(); | |
ValidateToken(TokenId.Colon, Res.ColonExpected); | |
NextToken(); | |
Expression expr2 = ParseExpression(); | |
expr = GenerateConditional(expr, expr1, expr2, errorPos); | |
} | |
return expr; | |
} | |
// ||, or operator | |
Expression ParseLogicalOr() { | |
Expression left = ParseLogicalAnd(); | |
while (token.id == TokenId.DoubleBar || TokenIdentifierIs("or")) { | |
Token op = token; | |
NextToken(); | |
Expression right = ParseLogicalAnd(); | |
CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); | |
left = Expression.OrElse(left, right); | |
} | |
return left; | |
} | |
// &&, and operator | |
Expression ParseLogicalAnd() { | |
Expression left = ParseComparison(); | |
while (token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and")) { | |
Token op = token; | |
NextToken(); | |
Expression right = ParseComparison(); | |
CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); | |
left = Expression.AndAlso(left, right); | |
} | |
return left; | |
} | |
// =, ==, !=, <>, >, >=, <, <= operators | |
Expression ParseComparison() { | |
Expression left = ParseAdditive(); | |
while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual || | |
token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater || | |
token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual || | |
token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual) { | |
Token op = token; | |
NextToken(); | |
Expression right = ParseAdditive(); | |
bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual || | |
op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater; | |
if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType) { | |
if (left.Type != right.Type) { | |
if (left.Type.IsAssignableFrom(right.Type)) { | |
right = Expression.Convert(right, left.Type); | |
} | |
else if (right.Type.IsAssignableFrom(left.Type)) { | |
left = Expression.Convert(left, right.Type); | |
} | |
else { | |
throw IncompatibleOperandsError(op.text, left, right, op.pos); | |
} | |
} | |
} | |
else if (IsEnumType(left.Type) || IsEnumType(right.Type)) { | |
if (left.Type != right.Type) { | |
Expression e; | |
if ((e = PromoteExpression(right, left.Type, true)) != null) { | |
right = e; | |
} | |
else if ((e = PromoteExpression(left, right.Type, true)) != null) { | |
left = e; | |
} | |
else { | |
throw IncompatibleOperandsError(op.text, left, right, op.pos); | |
} | |
} | |
} | |
else { | |
CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), | |
op.text, ref left, ref right, op.pos); | |
} | |
switch (op.id) { | |
case TokenId.Equal: | |
case TokenId.DoubleEqual: | |
left = GenerateEqual(left, right); | |
break; | |
case TokenId.ExclamationEqual: | |
case TokenId.LessGreater: | |
left = GenerateNotEqual(left, right); | |
break; | |
case TokenId.GreaterThan: | |
left = GenerateGreaterThan(left, right); | |
break; | |
case TokenId.GreaterThanEqual: | |
left = GenerateGreaterThanEqual(left, right); | |
break; | |
case TokenId.LessThan: | |
left = GenerateLessThan(left, right); | |
break; | |
case TokenId.LessThanEqual: | |
left = GenerateLessThanEqual(left, right); | |
break; | |
} | |
} | |
return left; | |
} | |
// +, -, & operators | |
Expression ParseAdditive() { | |
Expression left = ParseMultiplicative(); | |
while (token.id == TokenId.Plus || token.id == TokenId.Minus || | |
token.id == TokenId.Amphersand) { | |
Token op = token; | |
NextToken(); | |
Expression right = ParseMultiplicative(); | |
switch (op.id) { | |
case TokenId.Plus: | |
if (left.Type == typeof(string) || right.Type == typeof(string)) | |
goto case TokenId.Amphersand; | |
CheckAndPromoteOperands(typeof(IAddSignatures), op.text, ref left, ref right, op.pos); | |
left = GenerateAdd(left, right); | |
break; | |
case TokenId.Minus: | |
CheckAndPromoteOperands(typeof(ISubtractSignatures), op.text, ref left, ref right, op.pos); | |
left = GenerateSubtract(left, right); | |
break; | |
case TokenId.Amphersand: | |
left = GenerateStringConcat(left, right); | |
break; | |
} | |
} | |
return left; | |
} | |
// *, /, %, mod operators | |
Expression ParseMultiplicative() { | |
Expression left = ParseUnary(); | |
while (token.id == TokenId.Asterisk || token.id == TokenId.Slash || | |
token.id == TokenId.Percent || TokenIdentifierIs("mod")) { | |
Token op = token; | |
NextToken(); | |
Expression right = ParseUnary(); | |
CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos); | |
switch (op.id) { | |
case TokenId.Asterisk: | |
left = Expression.Multiply(left, right); | |
break; | |
case TokenId.Slash: | |
left = Expression.Divide(left, right); | |
break; | |
case TokenId.Percent: | |
case TokenId.Identifier: | |
left = Expression.Modulo(left, right); | |
break; | |
} | |
} | |
return left; | |
} | |
// -, !, not unary operators | |
Expression ParseUnary() { | |
if (token.id == TokenId.Minus || token.id == TokenId.Exclamation || | |
TokenIdentifierIs("not")) { | |
Token op = token; | |
NextToken(); | |
if (op.id == TokenId.Minus && (token.id == TokenId.IntegerLiteral || | |
token.id == TokenId.RealLiteral)) { | |
token.text = "-" + token.text; | |
token.pos = op.pos; | |
return ParsePrimary(); | |
} | |
Expression expr = ParseUnary(); | |
if (op.id == TokenId.Minus) { | |
CheckAndPromoteOperand(typeof(INegationSignatures), op.text, ref expr, op.pos); | |
expr = Expression.Negate(expr); | |
} | |
else { | |
CheckAndPromoteOperand(typeof(INotSignatures), op.text, ref expr, op.pos); | |
expr = Expression.Not(expr); | |
} | |
return expr; | |
} | |
return ParsePrimary(); | |
} | |
Expression ParsePrimary() { | |
Expression expr = ParsePrimaryStart(); | |
while (true) { | |
if (token.id == TokenId.Dot) { | |
NextToken(); | |
expr = ParseMemberAccess(null, expr); | |
} | |
else if (token.id == TokenId.OpenBracket) { | |
expr = ParseElementAccess(expr); | |
} | |
else { | |
break; | |
} | |
} | |
return expr; | |
} | |
Expression ParsePrimaryStart() { | |
switch (token.id) { | |
case TokenId.Identifier: | |
return ParseIdentifier(); | |
case TokenId.StringLiteral: | |
return ParseStringLiteral(); | |
case TokenId.IntegerLiteral: | |
return ParseIntegerLiteral(); | |
case TokenId.RealLiteral: | |
return ParseRealLiteral(); | |
case TokenId.OpenParen: | |
return ParseParenExpression(); | |
default: | |
throw ParseError(Res.ExpressionExpected); | |
} | |
} | |
Expression ParseStringLiteral() { | |
ValidateToken(TokenId.StringLiteral); | |
char quote = token.text[0]; | |
string s = token.text.Substring(1, token.text.Length - 2); | |
int start = 0; | |
while (true) { | |
int i = s.IndexOf(quote, start); | |
if (i < 0) break; | |
s = s.Remove(i, 1); | |
start = i + 1; | |
} | |
if (quote == '\'') { | |
if (s.Length != 1) | |
throw ParseError(Res.InvalidCharacterLiteral); | |
NextToken(); | |
return CreateLiteral(s[0], s); | |
} | |
NextToken(); | |
return CreateLiteral(s, s); | |
} | |
Expression ParseIntegerLiteral() { | |
ValidateToken(TokenId.IntegerLiteral); | |
string text = token.text; | |
if (text[0] != '-') { | |
ulong value; | |
if (!UInt64.TryParse(text, out value)) | |
throw ParseError(Res.InvalidIntegerLiteral, text); | |
NextToken(); | |
if (value <= (ulong)Int32.MaxValue) return CreateLiteral((int)value, text); | |
if (value <= (ulong)UInt32.MaxValue) return CreateLiteral((uint)value, text); | |
if (value <= (ulong)Int64.MaxValue) return CreateLiteral((long)value, text); | |
return CreateLiteral(value, text); | |
} | |
else { | |
long value; | |
if (!Int64.TryParse(text, out value)) | |
throw ParseError(Res.InvalidIntegerLiteral, text); | |
NextToken(); | |
if (value >= Int32.MinValue && value <= Int32.MaxValue) | |
return CreateLiteral((int)value, text); | |
return CreateLiteral(value, text); | |
} | |
} | |
Expression ParseRealLiteral() { | |
ValidateToken(TokenId.RealLiteral); | |
string text = token.text; | |
object value = null; | |
char last = text[text.Length - 1]; | |
if (last == 'F' || last == 'f') { | |
float f; | |
if (Single.TryParse(text.Substring(0, text.Length - 1), out f)) value = f; | |
} | |
else { | |
double d; | |
if (Double.TryParse(text, out d)) value = d; | |
} | |
if (value == null) throw ParseError(Res.InvalidRealLiteral, text); | |
NextToken(); | |
return CreateLiteral(value, text); | |
} | |
Expression CreateLiteral(object value, string text) { | |
ConstantExpression expr = Expression.Constant(value); | |
literals.Add(expr, text); | |
return expr; | |
} | |
Expression ParseParenExpression() { | |
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); | |
NextToken(); | |
Expression e = ParseExpression(); | |
ValidateToken(TokenId.CloseParen, Res.CloseParenOrOperatorExpected); | |
NextToken(); | |
return e; | |
} | |
Expression ParseIdentifier() { | |
ValidateToken(TokenId.Identifier); | |
object value; | |
if (keywords.TryGetValue(token.text, out value)) { | |
if (value is Type) return ParseTypeAccess((Type)value); | |
if (value == (object)keywordIt) return ParseIt(); | |
if (value == (object)keywordIif) return ParseIif(); | |
if (value == (object)keywordNew) return ParseNew(); | |
NextToken(); | |
return (Expression)value; | |
} | |
if (symbols.TryGetValue(token.text, out value) || | |
externals != null && externals.TryGetValue(token.text, out value)) { | |
Expression expr = value as Expression; | |
if (expr == null) { | |
expr = Expression.Constant(value); | |
} | |
else { | |
LambdaExpression lambda = expr as LambdaExpression; | |
if (lambda != null) return ParseLambdaInvocation(lambda); | |
} | |
NextToken(); | |
return expr; | |
} | |
if (it != null) return ParseMemberAccess(null, it); | |
throw ParseError(Res.UnknownIdentifier, token.text); | |
} | |
Expression ParseIt() { | |
if (it == null) | |
throw ParseError(Res.NoItInScope); | |
NextToken(); | |
return it; | |
} | |
Expression ParseIif() { | |
int errorPos = token.pos; | |
NextToken(); | |
Expression[] args = ParseArgumentList(); | |
if (args.Length != 3) | |
throw ParseError(errorPos, Res.IifRequiresThreeArgs); | |
return GenerateConditional(args[0], args[1], args[2], errorPos); | |
} | |
Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos) { | |
if (test.Type != typeof(bool)) | |
throw ParseError(errorPos, Res.FirstExprMustBeBool); | |
if (expr1.Type != expr2.Type) { | |
Expression expr1as2 = expr2 != nullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null; | |
Expression expr2as1 = expr1 != nullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null; | |
if (expr1as2 != null && expr2as1 == null) { | |
expr1 = expr1as2; | |
} | |
else if (expr2as1 != null && expr1as2 == null) { | |
expr2 = expr2as1; | |
} | |
else { | |
string type1 = expr1 != nullLiteral ? expr1.Type.Name : "null"; | |
string type2 = expr2 != nullLiteral ? expr2.Type.Name : "null"; | |
if (expr1as2 != null && expr2as1 != null) | |
throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2); | |
throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2); | |
} | |
} | |
return Expression.Condition(test, expr1, expr2); | |
} | |
Expression ParseNew() { | |
NextToken(); | |
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); | |
NextToken(); | |
List<DynamicProperty> properties = new List<DynamicProperty>(); | |
List<Expression> expressions = new List<Expression>(); | |
while (true) { | |
int exprPos = token.pos; | |
Expression expr = ParseExpression(); | |
string propName; | |
if (TokenIdentifierIs("as")) { | |
NextToken(); | |
propName = GetIdentifier(); | |
NextToken(); | |
} | |
else { | |
MemberExpression me = expr as MemberExpression; | |
if (me == null) throw ParseError(exprPos, Res.MissingAsClause); | |
propName = me.Member.Name; | |
} | |
expressions.Add(expr); | |
properties.Add(new DynamicProperty(propName, expr.Type)); | |
if (token.id != TokenId.Comma) break; | |
NextToken(); | |
} | |
ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); | |
NextToken(); | |
Type type = DynamicExpression.CreateClass(properties); | |
MemberBinding[] bindings = new MemberBinding[properties.Count]; | |
for (int i = 0; i < bindings.Length; i++) | |
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]); | |
return Expression.MemberInit(Expression.New(type), bindings); | |
} | |
Expression ParseLambdaInvocation(LambdaExpression lambda) { | |
int errorPos = token.pos; | |
NextToken(); | |
Expression[] args = ParseArgumentList(); | |
MethodBase method; | |
if (FindMethod(lambda.Type, "Invoke", false, args, out method) != 1) | |
throw ParseError(errorPos, Res.ArgsIncompatibleWithLambda); | |
return Expression.Invoke(lambda, args); | |
} | |
Expression ParseTypeAccess(Type type) { | |
int errorPos = token.pos; | |
NextToken(); | |
if (token.id == TokenId.Question) { | |
if (!type.IsValueType || IsNullableType(type)) | |
throw ParseError(errorPos, Res.TypeHasNoNullableForm, GetTypeName(type)); | |
type = typeof(Nullable<>).MakeGenericType(type); | |
NextToken(); | |
} | |
if (token.id == TokenId.OpenParen) { | |
Expression[] args = ParseArgumentList(); | |
MethodBase method; | |
switch (FindBestMethod(type.GetConstructors(), args, out method)) { | |
case 0: | |
if (args.Length == 1) | |
return GenerateConversion(args[0], type, errorPos); | |
throw ParseError(errorPos, Res.NoMatchingConstructor, GetTypeName(type)); | |
case 1: | |
return Expression.New((ConstructorInfo)method, args); | |
default: | |
throw ParseError(errorPos, Res.AmbiguousConstructorInvocation, GetTypeName(type)); | |
} | |
} | |
ValidateToken(TokenId.Dot, Res.DotOrOpenParenExpected); | |
NextToken(); | |
return ParseMemberAccess(type, null); | |
} | |
Expression GenerateConversion(Expression expr, Type type, int errorPos) { | |
Type exprType = expr.Type; | |
if (exprType == type) return expr; | |
if (exprType.IsValueType && type.IsValueType) { | |
if ((IsNullableType(exprType) || IsNullableType(type)) && | |
GetNonNullableType(exprType) == GetNonNullableType(type)) | |
return Expression.Convert(expr, type); | |
if ((IsNumericType(exprType) || IsEnumType(exprType)) && | |
(IsNumericType(type)) || IsEnumType(type)) | |
return Expression.ConvertChecked(expr, type); | |
} | |
if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) || | |
exprType.IsInterface || type.IsInterface) | |
return Expression.Convert(expr, type); | |
throw ParseError(errorPos, Res.CannotConvertValue, | |
GetTypeName(exprType), GetTypeName(type)); | |
} | |
Expression ParseMemberAccess(Type type, Expression instance) { | |
if (instance != null) type = instance.Type; | |
int errorPos = token.pos; | |
string id = GetIdentifier(); | |
NextToken(); | |
if (token.id == TokenId.OpenParen) { | |
if (instance != null && type != typeof(string)) { | |
Type enumerableType = FindGenericType(typeof(IEnumerable<>), type); | |
if (enumerableType != null) { | |
Type elementType = enumerableType.GetGenericArguments()[0]; | |
return ParseAggregate(instance, elementType, id, errorPos); | |
} | |
} | |
Expression[] args = ParseArgumentList(); | |
MethodBase mb; | |
switch (FindMethod(type, id, instance == null, args, out mb)) { | |
case 0: | |
throw ParseError(errorPos, Res.NoApplicableMethod, | |
id, GetTypeName(type)); | |
case 1: | |
MethodInfo method = (MethodInfo)mb; | |
if (!IsPredefinedType(method.DeclaringType)) | |
throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType)); | |
if (method.ReturnType == typeof(void)) | |
throw ParseError(errorPos, Res.MethodIsVoid, | |
id, GetTypeName(method.DeclaringType)); | |
return Expression.Call(instance, (MethodInfo)method, args); | |
default: | |
throw ParseError(errorPos, Res.AmbiguousMethodInvocation, | |
id, GetTypeName(type)); | |
} | |
} | |
else { | |
MemberInfo member = FindPropertyOrField(type, id, instance == null); | |
if (member == null) | |
throw ParseError(errorPos, Res.UnknownPropertyOrField, | |
id, GetTypeName(type)); | |
return member is PropertyInfo ? | |
Expression.Property(instance, (PropertyInfo)member) : | |
Expression.Field(instance, (FieldInfo)member); | |
} | |
} | |
static Type FindGenericType(Type generic, Type type) { | |
while (type != null && type != typeof(object)) { | |
if (type.IsGenericType && type.GetGenericTypeDefinition() == generic) return type; | |
if (generic.IsInterface) { | |
foreach (Type intfType in type.GetInterfaces()) { | |
Type found = FindGenericType(generic, intfType); | |
if (found != null) return found; | |
} | |
} | |
type = type.BaseType; | |
} | |
return null; | |
} | |
Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos) { | |
ParameterExpression outerIt = it; | |
ParameterExpression innerIt = Expression.Parameter(elementType, ""); | |
it = innerIt; | |
Expression[] args = ParseArgumentList(); | |
it = outerIt; | |
MethodBase signature; | |
if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1) | |
throw ParseError(errorPos, Res.NoApplicableAggregate, methodName); | |
Type[] typeArgs; | |
if (signature.Name == "Min" || signature.Name == "Max") { | |
typeArgs = new Type[] { elementType, args[0].Type }; | |
} | |
else { | |
typeArgs = new Type[] { elementType }; | |
} | |
if (args.Length == 0) { | |
args = new Expression[] { instance }; | |
} | |
else { | |
args = new Expression[] { instance, Expression.Lambda(args[0], innerIt) }; | |
} | |
return Expression.Call(typeof(Enumerable), signature.Name, typeArgs, args); | |
} | |
Expression[] ParseArgumentList() { | |
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); | |
NextToken(); | |
Expression[] args = token.id != TokenId.CloseParen ? ParseArguments() : new Expression[0]; | |
ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); | |
NextToken(); | |
return args; | |
} | |
Expression[] ParseArguments() { | |
List<Expression> argList = new List<Expression>(); | |
while (true) { | |
argList.Add(ParseExpression()); | |
if (token.id != TokenId.Comma) break; | |
NextToken(); | |
} | |
return argList.ToArray(); | |
} | |
Expression ParseElementAccess(Expression expr) { | |
int errorPos = token.pos; | |
ValidateToken(TokenId.OpenBracket, Res.OpenParenExpected); | |
NextToken(); | |
Expression[] args = ParseArguments(); | |
ValidateToken(TokenId.CloseBracket, Res.CloseBracketOrCommaExpected); | |
NextToken(); | |
if (expr.Type.IsArray) { | |
if (expr.Type.GetArrayRank() != 1 || args.Length != 1) | |
throw ParseError(errorPos, Res.CannotIndexMultiDimArray); | |
Expression index = PromoteExpression(args[0], typeof(int), true); | |
if (index == null) | |
throw ParseError(errorPos, Res.InvalidIndex); | |
return Expression.ArrayIndex(expr, index); | |
} | |
else { | |
MethodBase mb; | |
switch (FindIndexer(expr.Type, args, out mb)) { | |
case 0: | |
throw ParseError(errorPos, Res.NoApplicableIndexer, | |
GetTypeName(expr.Type)); | |
case 1: | |
return Expression.Call(expr, (MethodInfo)mb, args); | |
default: | |
throw ParseError(errorPos, Res.AmbiguousIndexerInvocation, | |
GetTypeName(expr.Type)); | |
} | |
} | |
} | |
static bool IsPredefinedType(Type type) { | |
foreach (Type t in predefinedTypes) if (t == type) return true; | |
return false; | |
} | |
static bool IsNullableType(Type type) { | |
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); | |
} | |
static Type GetNonNullableType(Type type) { | |
return IsNullableType(type) ? type.GetGenericArguments()[0] : type; | |
} | |
static string GetTypeName(Type type) { | |
Type baseType = GetNonNullableType(type); | |
string s = baseType.Name; | |
if (type != baseType) s += '?'; | |
return s; | |
} | |
static bool IsNumericType(Type type) { | |
return GetNumericTypeKind(type) != 0; | |
} | |
static bool IsSignedIntegralType(Type type) { | |
return GetNumericTypeKind(type) == 2; | |
} | |
static bool IsUnsignedIntegralType(Type type) { | |
return GetNumericTypeKind(type) == 3; | |
} | |
static int GetNumericTypeKind(Type type) { | |
type = GetNonNullableType(type); | |
if (type.IsEnum) return 0; | |
switch (Type.GetTypeCode(type)) { | |
case TypeCode.Char: | |
case TypeCode.Single: | |
case TypeCode.Double: | |
case TypeCode.Decimal: | |
return 1; | |
case TypeCode.SByte: | |
case TypeCode.Int16: | |
case TypeCode.Int32: | |
case TypeCode.Int64: | |
return 2; | |
case TypeCode.Byte: | |
case TypeCode.UInt16: | |
case TypeCode.UInt32: | |
case TypeCode.UInt64: | |
return 3; | |
default: | |
return 0; | |
} | |
} | |
static bool IsEnumType(Type type) { | |
return GetNonNullableType(type).IsEnum; | |
} | |
void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos) { | |
Expression[] args = new Expression[] { expr }; | |
MethodBase method; | |
if (FindMethod(signatures, "F", false, args, out method) != 1) | |
throw ParseError(errorPos, Res.IncompatibleOperand, | |
opName, GetTypeName(args[0].Type)); | |
expr = args[0]; | |
} | |
void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos) { | |
Expression[] args = new Expression[] { left, right }; | |
MethodBase method; | |
if (FindMethod(signatures, "F", false, args, out method) != 1) | |
throw IncompatibleOperandsError(opName, left, right, errorPos); | |
left = args[0]; | |
right = args[1]; | |
} | |
Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos) { | |
return ParseError(pos, Res.IncompatibleOperands, | |
opName, GetTypeName(left.Type), GetTypeName(right.Type)); | |
} | |
MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess) { | |
BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | | |
(staticAccess ? BindingFlags.Static : BindingFlags.Instance); | |
foreach (Type t in SelfAndBaseTypes(type)) { | |
MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field, | |
flags, Type.FilterNameIgnoreCase, memberName); | |
if (members.Length != 0) return members[0]; | |
} | |
return null; | |
} | |
int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method) { | |
BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | | |
(staticAccess ? BindingFlags.Static : BindingFlags.Instance); | |
foreach (Type t in SelfAndBaseTypes(type)) { | |
MemberInfo[] members = t.FindMembers(MemberTypes.Method, | |
flags, Type.FilterNameIgnoreCase, methodName); | |
int count = FindBestMethod(members.Cast<MethodBase>(), args, out method); | |
if (count != 0) return count; | |
} | |
method = null; | |
return 0; | |
} | |
int FindIndexer(Type type, Expression[] args, out MethodBase method) { | |
foreach (Type t in SelfAndBaseTypes(type)) { | |
MemberInfo[] members = t.GetDefaultMembers(); | |
if (members.Length != 0) { | |
IEnumerable<MethodBase> methods = members. | |
OfType<PropertyInfo>(). | |
Select(p => (MethodBase)p.GetGetMethod()). | |
Where(m => m != null); | |
int count = FindBestMethod(methods, args, out method); | |
if (count != 0) return count; | |
} | |
} | |
method = null; | |
return 0; | |
} | |
static IEnumerable<Type> SelfAndBaseTypes(Type type) { | |
if (type.IsInterface) { | |
List<Type> types = new List<Type>(); | |
AddInterface(types, type); | |
return types; | |
} | |
return SelfAndBaseClasses(type); | |
} | |
static IEnumerable<Type> SelfAndBaseClasses(Type type) { | |
while (type != null) { | |
yield return type; | |
type = type.BaseType; | |
} | |
} | |
static void AddInterface(List<Type> types, Type type) { | |
if (!types.Contains(type)) { | |
types.Add(type); | |
foreach (Type t in type.GetInterfaces()) AddInterface(types, t); | |
} | |
} | |
class MethodData | |
{ | |
public MethodBase MethodBase; | |
public ParameterInfo[] Parameters; | |
public Expression[] Args; | |
} | |
int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method) { | |
MethodData[] applicable = methods. | |
Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }). | |
Where(m => IsApplicable(m, args)). | |
ToArray(); | |
if (applicable.Length > 1) { | |
applicable = applicable. | |
Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))). | |
ToArray(); | |
} | |
if (applicable.Length == 1) { | |
MethodData md = applicable[0]; | |
for (int i = 0; i < args.Length; i++) args[i] = md.Args[i]; | |
method = md.MethodBase; | |
} | |
else { | |
method = null; | |
} | |
return applicable.Length; | |
} | |
bool IsApplicable(MethodData method, Expression[] args) { | |
if (method.Parameters.Length != args.Length) return false; | |
Expression[] promotedArgs = new Expression[args.Length]; | |
for (int i = 0; i < args.Length; i++) { | |
ParameterInfo pi = method.Parameters[i]; | |
if (pi.IsOut) return false; | |
Expression promoted = PromoteExpression(args[i], pi.ParameterType, false); | |
if (promoted == null) return false; | |
promotedArgs[i] = promoted; | |
} | |
method.Args = promotedArgs; | |
return true; | |
} | |
Expression PromoteExpression(Expression expr, Type type, bool exact) { | |
if (expr.Type == type) return expr; | |
if (expr is ConstantExpression) { | |
ConstantExpression ce = (ConstantExpression)expr; | |
if (ce == nullLiteral) { | |
if (!type.IsValueType || IsNullableType(type)) | |
return Expression.Constant(null, type); | |
} | |
else { | |
string text; | |
if (literals.TryGetValue(ce, out text)) { | |
Type target = GetNonNullableType(type); | |
Object value = null; | |
switch (Type.GetTypeCode(ce.Type)) { | |
case TypeCode.Int32: | |
case TypeCode.UInt32: | |
case TypeCode.Int64: | |
case TypeCode.UInt64: | |
value = ParseNumber(text, target); | |
break; | |
case TypeCode.Double: | |
if (target == typeof(decimal)) value = ParseNumber(text, target); | |
break; | |
case TypeCode.String: | |
value = ParseEnum(text, target); | |
break; | |
} | |
if (value != null) | |
return Expression.Constant(value, type); | |
} | |
} | |
} | |
if (IsCompatibleWith(expr.Type, type)) { | |
if (type.IsValueType || exact) return Expression.Convert(expr, type); | |
return expr; | |
} | |
return null; | |
} | |
static object ParseNumber(string text, Type type) { | |
switch (Type.GetTypeCode(GetNonNullableType(type))) { | |
case TypeCode.SByte: | |
sbyte sb; | |
if (sbyte.TryParse(text, out sb)) return sb; | |
break; | |
case TypeCode.Byte: | |
byte b; | |
if (byte.TryParse(text, out b)) return b; | |
break; | |
case TypeCode.Int16: | |
short s; | |
if (short.TryParse(text, out s)) return s; | |
break; | |
case TypeCode.UInt16: | |
ushort us; | |
if (ushort.TryParse(text, out us)) return us; | |
break; | |
case TypeCode.Int32: | |
int i; | |
if (int.TryParse(text, out i)) return i; | |
break; | |
case TypeCode.UInt32: | |
uint ui; | |
if (uint.TryParse(text, out ui)) return ui; | |
break; | |
case TypeCode.Int64: | |
long l; | |
if (long.TryParse(text, out l)) return l; | |
break; | |
case TypeCode.UInt64: | |
ulong ul; | |
if (ulong.TryParse(text, out ul)) return ul; | |
break; | |
case TypeCode.Single: | |
float f; | |
if (float.TryParse(text, out f)) return f; | |
break; | |
case TypeCode.Double: | |
double d; | |
if (double.TryParse(text, out d)) return d; | |
break; | |
case TypeCode.Decimal: | |
decimal e; | |
if (decimal.TryParse(text, out e)) return e; | |
break; | |
} | |
return null; | |
} | |
static object ParseEnum(string name, Type type) { | |
if (type.IsEnum) { | |
MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field, | |
BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static, | |
Type.FilterNameIgnoreCase, name); | |
if (memberInfos.Length != 0) return ((FieldInfo)memberInfos[0]).GetValue(null); | |
} | |
return null; | |
} | |
static bool IsCompatibleWith(Type source, Type target) { | |
if (source == target) return true; | |
if (!target.IsValueType) return target.IsAssignableFrom(source); | |
Type st = GetNonNullableType(source); | |
Type tt = GetNonNullableType(target); | |
if (st != source && tt == target) return false; | |
TypeCode sc = st.IsEnum ? TypeCode.Object : Type.GetTypeCode(st); | |
TypeCode tc = tt.IsEnum ? TypeCode.Object : Type.GetTypeCode(tt); | |
switch (sc) { | |
case TypeCode.SByte: | |
switch (tc) { | |
case TypeCode.SByte: | |
case TypeCode.Int16: | |
case TypeCode.Int32: | |
case TypeCode.Int64: | |
case TypeCode.Single: | |
case TypeCode.Double: | |
case TypeCode.Decimal: | |
return true; | |
} | |
break; | |
case TypeCode.Byte: | |
switch (tc) { | |
case TypeCode.Byte: | |
case TypeCode.Int16: | |
case TypeCode.UInt16: | |
case TypeCode.Int32: | |
case TypeCode.UInt32: | |
case TypeCode.Int64: | |
case TypeCode.UInt64: | |
case TypeCode.Single: | |
case TypeCode.Double: | |
case TypeCode.Decimal: | |
return true; | |
} | |
break; | |
case TypeCode.Int16: | |
switch (tc) { | |
case TypeCode.Int16: | |
case TypeCode.Int32: | |
case TypeCode.Int64: | |
case TypeCode.Single: | |
case TypeCode.Double: | |
case TypeCode.Decimal: | |
return true; | |
} | |
break; | |
case TypeCode.UInt16: | |
switch (tc) { | |
case TypeCode.UInt16: | |
case TypeCode.Int32: | |
case TypeCode.UInt32: | |
case TypeCode.Int64: | |
case TypeCode.UInt64: | |
case TypeCode.Single: | |
case TypeCode.Double: | |
case TypeCode.Decimal: | |
return true; | |
} | |
break; | |
case TypeCode.Int32: | |
switch (tc) { | |
case TypeCode.Int32: | |
case TypeCode.Int64: | |
case TypeCode.Single: | |
case TypeCode.Double: | |
case TypeCode.Decimal: | |
return true; | |
} | |
break; | |
case TypeCode.UInt32: | |
switch (tc) { | |
case TypeCode.UInt32: | |
case TypeCode.Int64: | |
case TypeCode.UInt64: | |
case TypeCode.Single: | |
case TypeCode.Double: | |
case TypeCode.Decimal: | |
return true; | |
} | |
break; | |
case TypeCode.Int64: | |
switch (tc) { | |
case TypeCode.Int64: | |
case TypeCode.Single: | |
case TypeCode.Double: | |
case TypeCode.Decimal: | |
return true; | |
} | |
break; | |
case TypeCode.UInt64: | |
switch (tc) { | |
case TypeCode.UInt64: | |
case TypeCode.Single: | |
case TypeCode.Double: | |
case TypeCode.Decimal: | |
return true; | |
} | |
break; | |
case TypeCode.Single: | |
switch (tc) { | |
case TypeCode.Single: | |
case TypeCode.Double: | |
return true; | |
} | |
break; | |
default: | |
if (st == tt) return true; | |
break; | |
} | |
return false; | |
} | |
static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2) { | |
bool better = false; | |
for (int i = 0; i < args.Length; i++) { | |
int c = CompareConversions(args[i].Type, | |
m1.Parameters[i].ParameterType, | |
m2.Parameters[i].ParameterType); | |
if (c < 0) return false; | |
if (c > 0) better = true; | |
} | |
return better; | |
} | |
// Return 1 if s -> t1 is a better conversion than s -> t2 | |
// Return -1 if s -> t2 is a better conversion than s -> t1 | |
// Return 0 if neither conversion is better | |
static int CompareConversions(Type s, Type t1, Type t2) { | |
if (t1 == t2) return 0; | |
if (s == t1) return 1; | |
if (s == t2) return -1; | |
bool t1t2 = IsCompatibleWith(t1, t2); | |
bool t2t1 = IsCompatibleWith(t2, t1); | |
if (t1t2 && !t2t1) return 1; | |
if (t2t1 && !t1t2) return -1; | |
if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2)) return 1; | |
if (IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1)) return -1; | |
return 0; | |
} | |
Expression GenerateEqual(Expression left, Expression right) { | |
return Expression.Equal(left, right); | |
} | |
Expression GenerateNotEqual(Expression left, Expression right) { | |
return Expression.NotEqual(left, right); | |
} | |
Expression GenerateGreaterThan(Expression left, Expression right) { | |
if (left.Type == typeof(string)) { | |
return Expression.GreaterThan( | |
GenerateStaticMethodCall("Compare", left, right), | |
Expression.Constant(0) | |
); | |
} | |
return Expression.GreaterThan(left, right); | |
} | |
Expression GenerateGreaterThanEqual(Expression left, Expression right) { | |
if (left.Type == typeof(string)) { | |
return Expression.GreaterThanOrEqual( | |
GenerateStaticMethodCall("Compare", left, right), | |
Expression.Constant(0) | |
); | |
} | |
return Expression.GreaterThanOrEqual(left, right); | |
} | |
Expression GenerateLessThan(Expression left, Expression right) { | |
if (left.Type == typeof(string)) { | |
return Expression.LessThan( | |
GenerateStaticMethodCall("Compare", left, right), | |
Expression.Constant(0) | |
); | |
} | |
return Expression.LessThan(left, right); | |
} | |
Expression GenerateLessThanEqual(Expression left, Expression right) { | |
if (left.Type == typeof(string)) { | |
return Expression.LessThanOrEqual( | |
GenerateStaticMethodCall("Compare", left, right), | |
Expression.Constant(0) | |
); | |
} | |
return Expression.LessThanOrEqual(left, right); | |
} | |
Expression GenerateAdd(Expression left, Expression right) { | |
if (left.Type == typeof(string) && right.Type == typeof(string)) { | |
return GenerateStaticMethodCall("Concat", left, right); | |
} | |
return Expression.Add(left, right); | |
} | |
Expression GenerateSubtract(Expression left, Expression right) { | |
return Expression.Subtract(left, right); | |
} | |
Expression GenerateStringConcat(Expression left, Expression right) { | |
return Expression.Call( | |
null, | |
typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }), | |
new[] { left, right }); | |
} | |
MethodInfo GetStaticMethod(string methodName, Expression left, Expression right) { | |
return left.Type.GetMethod(methodName, new[] { left.Type, right.Type }); | |
} | |
Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right) { | |
return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right }); | |
} | |
void SetTextPos(int pos) { | |
textPos = pos; | |
ch = textPos < textLen ? text[textPos] : '\0'; | |
} | |
void NextChar() { | |
if (textPos < textLen) textPos++; | |
ch = textPos < textLen ? text[textPos] : '\0'; | |
} | |
void NextToken() { | |
while (Char.IsWhiteSpace(ch)) NextChar(); | |
TokenId t; | |
int tokenPos = textPos; | |
switch (ch) { | |
case '!': | |
NextChar(); | |
if (ch == '=') { | |
NextChar(); | |
t = TokenId.ExclamationEqual; | |
} | |
else { | |
t = TokenId.Exclamation; | |
} | |
break; | |
case '%': | |
NextChar(); | |
t = TokenId.Percent; | |
break; | |
case '&': | |
NextChar(); | |
if (ch == '&') { | |
NextChar(); | |
t = TokenId.DoubleAmphersand; | |
} | |
else { | |
t = TokenId.Amphersand; | |
} | |
break; | |
case '(': | |
NextChar(); | |
t = TokenId.OpenParen; | |
break; | |
case ')': | |
NextChar(); | |
t = TokenId.CloseParen; | |
break; | |
case '*': | |
NextChar(); | |
t = TokenId.Asterisk; | |
break; | |
case '+': | |
NextChar(); | |
t = TokenId.Plus; | |
break; | |
case ',': | |
NextChar(); | |
t = TokenId.Comma; | |
break; | |
case '-': | |
NextChar(); | |
t = TokenId.Minus; | |
break; | |
case '.': | |
NextChar(); | |
t = TokenId.Dot; | |
break; | |
case '/': | |
NextChar(); | |
t = TokenId.Slash; | |
break; | |
case ':': | |
NextChar(); | |
t = TokenId.Colon; | |
break; | |
case '<': | |
NextChar(); | |
if (ch == '=') { | |
NextChar(); | |
t = TokenId.LessThanEqual; | |
} | |
else if (ch == '>') { | |
NextChar(); | |
t = TokenId.LessGreater; | |
} | |
else { | |
t = TokenId.LessThan; | |
} | |
break; | |
case '=': | |
NextChar(); | |
if (ch == '=') { | |
NextChar(); | |
t = TokenId.DoubleEqual; | |
} | |
else { | |
t = TokenId.Equal; | |
} | |
break; | |
case '>': | |
NextChar(); | |
if (ch == '=') { | |
NextChar(); | |
t = TokenId.GreaterThanEqual; | |
} | |
else { | |
t = TokenId.GreaterThan; | |
} | |
break; | |
case '?': | |
NextChar(); | |
t = TokenId.Question; | |
break; | |
case '[': | |
NextChar(); | |
t = TokenId.OpenBracket; | |
break; | |
case ']': | |
NextChar(); | |
t = TokenId.CloseBracket; | |
break; | |
case '|': | |
NextChar(); | |
if (ch == '|') { | |
NextChar(); | |
t = TokenId.DoubleBar; | |
} | |
else { | |
t = TokenId.Bar; | |
} | |
break; | |
case '"': | |
case '\'': | |
char quote = ch; | |
do { | |
NextChar(); | |
while (textPos < textLen && ch != quote) NextChar(); | |
if (textPos == textLen) | |
throw ParseError(textPos, Res.UnterminatedStringLiteral); | |
NextChar(); | |
} while (ch == quote); | |
t = TokenId.StringLiteral; | |
break; | |
default: | |
if (Char.IsLetter(ch) || ch == '@' || ch == '_') { | |
do { | |
NextChar(); | |
} while (Char.IsLetterOrDigit(ch) || ch == '_'); | |
t = TokenId.Identifier; | |
break; | |
} | |
if (Char.IsDigit(ch)) { | |
t = TokenId.IntegerLiteral; | |
do { | |
NextChar(); | |
} while (Char.IsDigit(ch)); | |
if (ch == '.') { | |
t = TokenId.RealLiteral; | |
NextChar(); | |
ValidateDigit(); | |
do { | |
NextChar(); | |
} while (Char.IsDigit(ch)); | |
} | |
if (ch == 'E' || ch == 'e') { | |
t = TokenId.RealLiteral; | |
NextChar(); | |
if (ch == '+' || ch == '-') NextChar(); | |
ValidateDigit(); | |
do { | |
NextChar(); | |
} while (Char.IsDigit(ch)); | |
} | |
if (ch == 'F' || ch == 'f') NextChar(); | |
break; | |
} | |
if (textPos == textLen) { | |
t = TokenId.End; | |
break; | |
} | |
throw ParseError(textPos, Res.InvalidCharacter, ch); | |
} | |
token.id = t; | |
token.text = text.Substring(tokenPos, textPos - tokenPos); | |
token.pos = tokenPos; | |
} | |
bool TokenIdentifierIs(string id) { | |
return token.id == TokenId.Identifier && String.Equals(id, token.text, StringComparison.OrdinalIgnoreCase); | |
} | |
string GetIdentifier() { | |
ValidateToken(TokenId.Identifier, Res.IdentifierExpected); | |
string id = token.text; | |
if (id.Length > 1 && id[0] == '@') id = id.Substring(1); | |
return id; | |
} | |
void ValidateDigit() { | |
if (!Char.IsDigit(ch)) throw ParseError(textPos, Res.DigitExpected); | |
} | |
void ValidateToken(TokenId t, string errorMessage) { | |
if (token.id != t) throw ParseError(errorMessage); | |
} | |
void ValidateToken(TokenId t) { | |
if (token.id != t) throw ParseError(Res.SyntaxError); | |
} | |
Exception ParseError(string format, params object[] args) { | |
return ParseError(token.pos, format, args); | |
} | |
Exception ParseError(int pos, string format, params object[] args) { | |
return new ParseException(string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args), pos); | |
} | |
static Dictionary<string, object> CreateKeywords() { | |
Dictionary<string, object> d = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); | |
d.Add("true", trueLiteral); | |
d.Add("false", falseLiteral); | |
d.Add("null", nullLiteral); | |
d.Add(keywordIt, keywordIt); | |
d.Add(keywordIif, keywordIif); | |
d.Add(keywordNew, keywordNew); | |
foreach (Type type in predefinedTypes) d.Add(type.Name, type); | |
return d; | |
} | |
} | |
static class Res | |
{ | |
public const string DuplicateIdentifier = "The identifier '{0}' was defined more than once"; | |
public const string ExpressionTypeMismatch = "Expression of type '{0}' expected"; | |
public const string ExpressionExpected = "Expression expected"; | |
public const string InvalidCharacterLiteral = "Character literal must contain exactly one character"; | |
public const string InvalidIntegerLiteral = "Invalid integer literal '{0}'"; | |
public const string InvalidRealLiteral = "Invalid real literal '{0}'"; | |
public const string UnknownIdentifier = "Unknown identifier '{0}'"; | |
public const string NoItInScope = "No 'it' is in scope"; | |
public const string IifRequiresThreeArgs = "The 'iif' function requires three arguments"; | |
public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'"; | |
public const string BothTypesConvertToOther = "Both of the types '{0}' and '{1}' convert to the other"; | |
public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other"; | |
public const string MissingAsClause = "Expression is missing an 'as' clause"; | |
public const string ArgsIncompatibleWithLambda = "Argument list incompatible with lambda expression"; | |
public const string TypeHasNoNullableForm = "Type '{0}' has no nullable form"; | |
public const string NoMatchingConstructor = "No matching constructor in type '{0}'"; | |
public const string AmbiguousConstructorInvocation = "Ambiguous invocation of '{0}' constructor"; | |
public const string CannotConvertValue = "A value of type '{0}' cannot be converted to type '{1}'"; | |
public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'"; | |
public const string MethodsAreInaccessible = "Methods on type '{0}' are not accessible"; | |
public const string MethodIsVoid = "Method '{0}' in type '{1}' does not return a value"; | |
public const string AmbiguousMethodInvocation = "Ambiguous invocation of method '{0}' in type '{1}'"; | |
public const string UnknownPropertyOrField = "No property or field '{0}' exists in type '{1}'"; | |
public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists"; | |
public const string CannotIndexMultiDimArray = "Indexing of multi-dimensional arrays is not supported"; | |
public const string InvalidIndex = "Array index must be an integer expression"; | |
public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'"; | |
public const string AmbiguousIndexerInvocation = "Ambiguous invocation of indexer in type '{0}'"; | |
public const string IncompatibleOperand = "Operator '{0}' incompatible with operand type '{1}'"; | |
public const string IncompatibleOperands = "Operator '{0}' incompatible with operand types '{1}' and '{2}'"; | |
public const string UnterminatedStringLiteral = "Unterminated string literal"; | |
public const string InvalidCharacter = "Syntax error '{0}'"; | |
public const string DigitExpected = "Digit expected"; | |
public const string SyntaxError = "Syntax error"; | |
public const string TokenExpected = "{0} expected"; | |
public const string ParseExceptionFormat = "{0} (at index {1})"; | |
public const string ColonExpected = "':' expected"; | |
public const string OpenParenExpected = "'(' expected"; | |
public const string CloseParenOrOperatorExpected = "')' or operator expected"; | |
public const string CloseParenOrCommaExpected = "')' or ',' expected"; | |
public const string DotOrOpenParenExpected = "'.' or '(' expected"; | |
public const string OpenBracketExpected = "'[' expected"; | |
public const string CloseBracketOrCommaExpected = "']' or ',' expected"; | |
public const string IdentifierExpected = "Identifier expected"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment