ã¢ã¹ãã¯ãæåã®ããã°ã©ãã³ã°ã¯ãã³ãŒãããŒã¹ãç°¡çŽ åããã¯ãªãŒã³ãªã³ãŒããçæããã³ããŒãšè²Œãä»ãã®ãšã©ãŒãæå°éã«æããããã®éåžžã«é åçãªæŠå¿µã§ãã
ä»æ¥ãã»ãšãã©ã®å Žåãã¢ã¹ãã¯ãã¯ãã€ãã³ãŒãã¬ãã«ã§å®è£ ãããŠããŸããã³ã³ãã€ã«åŸãäžéšã®ããŒã«ã¯ãå¿ èŠãªããžãã¯ããµããŒãããŠè¿œå ã®ãã€ãã³ãŒãããç¹ã亀ãããŸãã
ç§ãã¡ã®ã¢ãããŒãïŒããã³ä»ã®ããã€ãã®ããŒã«ã®ã¢ãããŒãïŒã¯ãã¢ã¹ãã¯ãããžãã¯ãå®è£ ããããã«ãœãŒã¹ã³ãŒããå€æŽããŠããŸãã Roslynãã¯ãããžãŒãžã®ç§»è¡ã«ããããããå®çŸããã®ã¯éåžžã«ç°¡åã§ããããã®çµæããã€ãã³ãŒãèªäœã®å€æŽã«æ¯ã¹ãŠç¹å®ã®å©ç¹ãåŸãããŸãã
詳现ã«èå³ã®ããæ¹ã¯ç«ãã芧ãã ããã
ã¢ã¹ãã¯ãæåã®ããã°ã©ãã³ã°ã¯ããªãã«é¢ãããã®ã§ã¯ãªããç¹ã«ããªãã«é¢ä¿ããããšã¯æããªããããããŸãããç解ã§ããªãèšèã®éãŸãã§ãããå®éã«ã¯ãèŠãç®ãããã¯ããã«ç°¡åã§ããããã¯å®éã®è£œåéçºã®åé¡ã«é¢ãããã®ã§ãããç£æ¥éçºã«åŸäºããŠããå Žåã¯ãééããªãããã䜿çšããããšããå©çãåŸãã
ç¹ã«ã補åã®æ©èœèŠä»¶ã圢åŒåãããŠããäŒæ¥ã¬ãã«ã®äžèŠæš¡ãããžã§ã¯ãã§ã¯ãããšãã°ãèŠä»¶ãããå ŽåããããŸããæ§æãã©ã°ãèšå®ãããšãã«ããã¹ãŠã®ãããªãã¯ã¡ãœããã®ãã¹ãŠã®å ¥åãã©ã¡ãŒã¿ããã°ã«èšé²ããŸãããŸãã¯ããã¹ãŠã®ãããžã§ã¯ãã¡ãœããã«ã€ããŠããã®ã¡ãœããã®å®è¡æéã®ç¹å®ã®ãããå€ãè¶ ãããšãã«ã¡ãã»ãŒãžãéä¿¡ããéç¥ã·ã¹ãã ãçšæããŸãã
ããã¯AOPãªãã§ã©ã®ããã«è¡ãããŸããïŒãã³ããŒã§æã¡èŸŒãŸããæãéèŠãªéšåã«å¯ŸããŠã®ã¿å®è¡ãããããæ°ããã¡ãœãããäœæãããšãã«ãé£æ¥ããã¡ãœããããåæ§ã®ã³ãŒããã³ããŒããŠè²Œãä»ããä»éãããã¹ãŠã®ã¡ãœããã䜿çšããŸãã
AOPã䜿çšããå Žåããããžã§ã¯ãã«é©çšãããŠãžã§ããå®äºãããšãã¢ããã€ã¹ãæžã蟌ãŸããŸããããžãã¯ãå°ãæŽæ°ããå¿ èŠãããå Žåã¯ãã¢ããã€ã¹ãããäžåºŠæŽæ°ãããšã次ã®ãã«ãã«é©çšãããŸãã AOPããªãå Žåããããžã§ã¯ãã³ãŒãå šäœã§100,500åã®æŽæ°ã«ãªããŸãã
ãã©ã¹ã¯ããã®ãããªæ©èœãæ£ãã°ã£ãŠããŠãã³ãŒããèªããšãã«è¿·æãªãã€ãºã®ããã«èŠãããããã³ãŒããsmallpoxãæã£ãŠãã人ã®ããã«èŠããªããªãããšã§ãã
AOPããããžã§ã¯ãã«å°å ¥ããåŸãAOPããªããšå€¢ã«ãæããªãã£ããã®ãå®è£ ãå§ããŸããããã¯ãAOPãæ¯èŒçå°ããªå©ç¹ã®ããã«èŠãã倧ããªã³ã¹ãããããããã§ãã AOPã䜿çšãããšããã¹ãŠãæ£å察ã§ãæ¯èŒçäœã³ã¹ãã§å€§ããªã¡ãªããããããŸãïŒåãã¬ãã«ã®åªåã®ã³ã¹ãã«å¯ŸããŠïŒã
ç§ã®æãã§ã¯ãã¢ã¹ãã¯ãæåã®ããã°ã©ãã³ã°ã¯ãJavaãšã³ã·ã¹ãã ãšæ¯èŒããŠ.Netãšã³ã·ã¹ãã ã§ã¯ããŸã人æ°ããããŸãããäž»ãªçç±ã¯ãJavaã®æ©èœãšå質ã«å¹æµããç¡æã®ãªãŒãã³ãœãŒã¹ããŒã«ããªãããšã ãšæããŸãã
PostSharpã¯åæ§ã®æ©èœãšå©äŸ¿æ§ãæäŸããŸããããããžã§ã¯ãã§äœ¿çšããããã«æ°çŸãã«ãæ¯æãããšãããšããªã人ã¯å€ããªããã³ãã¥ããã£ããŒãžã§ã³ã®æ©èœã¯éåžžã«éãããŠããŸãããã¡ããã代æ¿æ段ã¯ãããŸãããæ®å¿µãªãããPostSharpã®ã¬ãã«ã«ã¯éããŠããŸãããããŒã«ã®æ©èœãæ¯èŒ
ã§ããŸãïŒæ¯èŒã¯PostSharpã®ææè ã«ãã£ãŠè¡ãããããšãèŠããŠããå¿ èŠããããŸãããããçšåºŠã®ç¶æ³ãããããŸãïŒã
ã¢ã¹ãã¯ãæåããã°ã©ãã³ã°ãžã®é
ç§ãã¡ã¯å°ããªã³ã³ãµã«ãã£ã³ã°äŒç€ŸïŒ12人ïŒã§ãããç§ãã¡ã®ä»äºã®æçµçµæã¯ãœãŒã¹ã³ãŒãã§ãããããããœãŒã¹ã³ãŒããå質ã³ãŒããäœæããããã«å ±é ¬ãåãåããŸããç§ãã¡ã¯1ã€ã®æ¥çã§ããåããŠãããããããžã§ã¯ãã®å€ãã«ã¯éåžžã«é¡äŒŒããèŠä»¶ãããããã®çµæããœãŒã¹ã³ãŒãããããã®ãããžã§ã¯ãéã§éåžžã«é¡äŒŒããŠããŸãã
ãŸãããªãœãŒã¹ãéãããŠãããããç§ãã¡ã«ãšã£ãŠæãéèŠãªã¿ã¹ã¯ã®1ã€ã¯ãã³ãŒããåå©çšããéçºè ãæ¥åžžçãªã¿ã¹ã¯ããæãããŒã«ã䜿çšããæ©èœã§ãã
ãããå®çŸããæ¹æ³ã®1ã€ã¯ãèªåã³ãŒãçææ©èœãå€çšãããããžã§ã¯ããšã¿ã¹ã¯ã«åºæã®VisualStudioçšã®ã«ã¹ã¿ã ãã©ã°ã€ã³ãšã¢ãã©ã€ã¶ãŒãããã€ãäœæããããšã§ããããã«ãããé«ãã³ãŒãå質ãç¶æããªãããããã°ã©ããŒã®çç£æ§ãå€§å¹ ã«åäžãããããšãã§ããŸããïŒå質ãåäžãããšããèšããŸãïŒã
次ã®è«ççãªã¹ãããã¯ãã¢ã¹ãã¯ãæåã®ããã°ã©ãã³ã°ã®äœ¿çšãå®è£ ãããšããã¢ã€ãã¢ã§ãããããã€ãã®ã¢ãããŒããšããŒã«ãè©ŠããŸããããçµæã¯ç§ãã¡ã®æåŸ ããã¯ã»ã©é ããã®ã§ãããããã¯Roslynãã¯ãããžãŒã®ãªãªãŒã¹ãšåæã«è¡ãããããæç¹ã§ãèªåã³ãŒãçæãšRoslynã®æ©èœãçµã¿åããããšããã¢ã€ãã¢ããããŸããã
ãããæ°é±éã§ã楜åšã®ãããã¿ã€ããäœæãããç§ãã¡ã®æ°æã¡ã«ããã°ããã®ã¢ãããŒãã¯ããææã§ããããã«æãããŸããããã®ããŒã«ã®äœ¿çšæ¹æ³ãšæŽæ°æ¹æ³ãäœåºŠãç¹°ãè¿ããçµæãç§ãã¡ã®æåŸ ã¯æºããããäºæ³ãã¯ããã«è¶ ãããšèšããŸãã䟿å©ãªãã³ãã¬ãŒãã®ã©ã€ãã©ãªãéçºããŸãããã»ãšãã©ã®ãããžã§ã¯ãã§ãã®ã¢ãããŒãã䜿çšããŠãããäžéšã®ã¯ã©ã€ã¢ã³ããããã䜿çšããŠãããŒãºã«åãããŠãã³ãã¬ãŒãã®éçºã泚æããŠããŸãã
æ®å¿µãªãããç§ãã¡ã®ããŒã«ã¯ãŸã çæ³ããã»ã©é ãã®ã§ã説æã2ã€ã®éšåã«åããããšæããŸãã1ã€ã¯çæ³çãªäžçã§ã®ãã®æ©èœã®å®è£ ã®èŠæ¹ã§ããã2ã€ç®ã¯ããã§ã®æ¹æ³ã§ãã
詳现ã«ç§»ãåã«ãç°¡åãªèª¬æãããããšæããŸãããã®èšäºã®ãã¹ãŠã®äŸã¯ãç¡é¢ä¿ãªè©³çŽ°ã§éè² è·ã«ãªãããšãªããã¢ã€ãã¢ã瀺ãããšãã§ããã¬ãã«ã«ç°¡ç¥åãããŠããŸãã
å®ç§ãªäžçã§ãããã©ã®ããã«è¡ãããã
ç§ãã¡ã®ããŒã«ãæ°å¹Žé䜿çšããåŸãç§ã¯çæ³çãªäžçã«äœãã§ããå Žåã«ãããã©ã®ããã«æ©èœããããããšããããžã§ã³ãæã£ãŠããŸãã
çæ³çãªäžçã®ç§ã®ããžã§ã³ã§ã¯ãèšèªä»æ§ã¯ãœãŒã¹ã³ãŒãå€æã®äœ¿çšãèš±å¯ããã³ã³ãã€ã©ãšIDEã®ãµããŒãããããŸãã
ãã®ã¢ã€ãã¢ã¯ãCïŒèšèªä»æ§ã«ãéšåçã修食åãå«ããããšã«è§ŠçºãããŸããããã®ããªãåçŽãªæŠå¿µïŒè€æ°ã®ãã¡ã€ã«ã§ã¯ã©ã¹ãæ§é ããŸãã¯ã€ã³ã¿ãŒãã§ã€ã¹ãå®çŸ©ããæ©èœïŒã«ãããèªåãœãŒã¹ã³ãŒãçæããŒã«ã®ãµããŒããåçã«æ¹åããã³ç°¡çŽ åãããŸãããããããããã¯ãã¯ã©ã¹ã®ãœãŒã¹ã³ãŒããè€æ°ã®ãã¡ã€ã«ã«æ°Žå¹³æ¹åã«åå²ãããããªãã®ã§ãã CïŒèšèªãç¥ããªã人ã®ããã«ãå°ããªäŸã
Example1.aspxãã¡ã€ã«ã«èšè¿°ãããŠããåçŽãªãã©ãŒã ããããšããŸãã
<%@ Page Language="C#" AutoEventWireup="True" %>
// . . .
<asp:Button id="btnSubmit"
Text="Submit"
OnClick=" btnSubmit_Click"
runat="server"/>
// . . .
ãããŠãExample1.aspx.csãã¡ã€ã«ã®ã«ã¹ã¿ã ããžãã¯ïŒããšãã°ããã¿ã³ãã¯ãªãã¯ããããšãã«ãã¿ã³ã®è²ãèµ€ã«å€æŽããïŒ
public partial class ExamplePage1 : System.Web.UI.Page, IMyInterface
{
protected void btnSubmit_Click(Object sender, EventArgs e)
{
btnSubmit.Color = Color.Red;
}
}
ãpartialãã«ãã£ãŠæäŸãããæ©èœã®èšèªã§ã®ååšã«ãããããŒã«ãããã¯Example1.aspxãã¡ã€ã«ã解æããExample1.aspx.designer.csãã¡ã€ã«ãèªåçã«çæã§ããŸãã
public partial class ExamplePage1 : System.Web.UI.Page
{
protected global::System.Web.UI.WebControls.Button btnSubmit;
}
ããããæŽæ°å¯èœãªããã°ã©ããŒïŒExample1.aspx.csïŒã«ãã£ãŠExamplePage1ã¯ã©ã¹ã®ã³ãŒãã®äžéšã1ã€ã®ãã¡ã€ã«ã«æ ŒçŽããèªåçæãããããŒã«ãããã«ãã£ãŠExample1.aspx.designer.csãã¡ã€ã«ã®äžéšãæ ŒçŽããããšãã§ããŸããã³ã³ãã€ã©ã«ãšã£ãŠãæçµçã«ã¯1ã€ã®äžè¬çãªã¯ã©ã¹ã®ããã«èŠããŸã
public class ExamplePage1 : System.Web.UI.Page, IMyInterface
{
protected global::System.Web.UI.WebControls.Button btnSubmit;
protected void btnSubmit_Click(Object sender, EventArgs e)
{
btnSubmit.Color = Color.Red;
}
}
IMyInterfaceã€ã³ã¿ãŒãã§ãŒã¹ã®ç¶æ¿ã®å®çŸ©ãå«ãäŸã䜿çšãããšãæçµçµæãç°ãªããã¡ã€ã«ããã®ã¯ã©ã¹å®çŸ©ã®çµã¿åããã§ããããšãããããŸãã
ããŒã·ã£ã«ãªã©ã®æ©èœããªããã³ã³ãã€ã©ããã¹ãŠã®ã¯ã©ã¹ã³ãŒãã1ã€ã®ãã¡ã€ã«ã«ã®ã¿ä¿åããå¿ èŠãããå ŽåãèªåçæããµããŒãããããã«å¿ èŠãªäžäŸ¿ããšè¿œå ã®ãžã§ã¹ãã£ãæ³å®ã§ããŸãã
ãããã£ãŠãç§ã®èãã¯ãèšèªä»æ§ã«2ã€ã®è¿œå ã®ä¿®é£Ÿåãå«ããããšã§ããããã«ããããœãŒã¹ã³ãŒãã«ã¢ã¹ãã¯ããç°¡åã«åã蟌ãããšãã§ããŸãã
æåã®ä¿®é£Ÿåã¯ãªãªãžãã«ã§ãããå€æã§ããã¯ãã®ã¯ã©ã¹å®çŸ©ã«è¿œå ããŸãã
2çªç®ã¯åŠçãããããããœãŒã¹å€æããŒã«ã«ãã£ãŠååŸãããæçµçãªã¯ã©ã¹å®çŸ©ã§ããããã€ãã³ãŒããçæããããã«ã³ã³ãã€ã©ã«ãã£ãŠåãå ¥ããããå¿ èŠãããããšã瀺ããŠããŸãã
ã·ãŒã±ã³ã¹ã¯ãããªæã
- ãŠãŒã¶ãŒã¯ã.csãã¡ã€ã«ã«å ã®ä¿®é£Ÿåãå«ãã¯ã©ã¹ã®ãœãŒã¹ã³ãŒããæäœããŸãïŒäŸïŒExample1.csïŒ
- ã³ã³ãã€ã«æã«ãã³ã³ãã€ã©ã¯ãœãŒã¹ã³ãŒãã®æ£ç¢ºãããã§ãã¯ããã¯ã©ã¹ãæ£åžžã«ã³ã³ãã€ã«ãããå Žåã¯ãå ã®ã³ãŒãã®ååšããã§ãã¯ããŸãã
- ãªãªãžãã«ãååšããå Žåãã³ã³ãã€ã©ã¯ãã®ãã¡ã€ã«ã®ãœãŒã¹ã³ãŒããå€æããã»ã¹ã«æž¡ããŸãïŒããã¯ã³ã³ãã€ã©ã®ãã©ãã¯ããã¯ã¹ã§ãïŒã
- .processed.cs .processed.cs.map ( .cs .processed.cs, IDE)
- .processed.cs ( Example1.processed.cs) .
- ,
a. original processed
b. .cs .processed.cs - , .processed.cs .
ããããããã2ã€ã®ä¿®é£Ÿåãè¿œå ããããšã§ããœãŒã¹ã³ãŒãçæã®ãµããŒããéšåçã«ç°¡çŽ åã§ããããã«ãèšèªã¬ãã«ã§ãœãŒã¹ã³ãŒãå€æããŒã«ã®ãµããŒããæŽçããããšãã§ããŸããããããã parialã¯æ°Žå¹³æ¹åã®ã³ãŒãåå²ã§ãããå ã®/åŠçããããã®ã¯åçŽæ¹åã§ãã
ç§ãèŠãŠããããã«ãã³ã³ãã€ã©ã§å ã®/åŠçããããµããŒããå®è£ ããããšã¯ãMicrosoftã®2人ã®ã€ã³ã¿ãŒã³ã«ãšã£ãŠ1é±éã®äœæ¥ã§ãïŒãã¡ããåè«ã§ãããçå®ããããé ãã¯ãããŸããïŒãæŠããŠããã®ã¿ã¹ã¯ã«ã¯åºæ¬çãªåé¡ã¯ãããŸãããã³ã³ãã€ã©ãŒã®èŠ³ç¹ããã¯ããã¡ã€ã«ã®æäœãšããã»ã¹ã®åŒã³åºãã§ãã
.NET 5ã§ã¯ãæ°ããæ©èœãè¿œå ãããŸãã-ãœãŒã¹ã³ãŒããžã§ãã¬ãŒã¿ãŒããã«ãããã³ã³ãã€ã«äžã«æ°ãããœãŒã¹ã³ãŒããã¡ã€ã«ãçæã§ããŸããããã¯æ£ããæ¹åãžã®åãã§ããæ®å¿µãªãããæ°ãããœãŒã¹ã³ãŒããçæããããšã¯ã§ããŸãããæ¢åã®ãœãŒã¹ã³ãŒããå€æŽããããšã¯ã§ããŸãããã ããç§ãã¡ã¯ãŸã åŸ ã£ãŠããŸãã
åæ§ã®ããã»ã¹ã®äŸããŠãŒã¶ãŒããã¡ã€ã«Example2.csãäœæããŸã
public original class ExamplePage2 : System.Web.UI.Page, IMyInterface
{
protected global::System.Web.UI.WebControls.Button btnSubmit;
protected void btnSubmit_Click(Object sender, EventArgs e)
{
btnSubmit.Color = Color.Red;
}
}
ã³ã³ãã€ã«ã®ããã«å®è¡ããããã¹ãŠã«åé¡ããªããã³ã³ãã€ã©ãå ã®ä¿®é£Ÿåã確èªãããšãå€æããã»ã¹ã«ãœãŒã¹ã³ãŒããæž¡ãããExample2.processed.csãã¡ã€ã«ãçæãããŸãïŒæãåçŽãªã±ãŒã¹ã§ã¯ãå ã®ãã¡ã€ã«ãåŠçæžã¿ã«çœ®ãæããããExample2.csã®æ£ç¢ºãªã³ããŒã«ãªããŸãïŒã ..ã
ãã®å Žåãå€æããã»ã¹ã§ãã®ã³ã°ã¢ã¹ãã¯ããè¿œå ãããçµæã¯æ¬¡ã®ããã«ãªããšæ³å®ããŸãã
public processed class ExamplePage2 : System.Web.UI.Page, IMyInterface
{
protected global::System.Web.UI.WebControls.Button btnSubmit;
protected void btnSubmit_Click(Object sender, EventArgs e)
{
try
{
btnSubmit.Color = Color.Red;
}
catch(Exception ex)
{
ErrorLog(ex);
throw;
}
SuccessLog();
}
private static processed ErrorLog(Exception ex)
{
// some error logic here
}
private static processed SuccessLog([System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
{
// some success logic here
}
}
次ã®ã¹ãããã¯ã眲åã確èªããããšã§ãã _main_眲åã¯åäžã§ãããå ã®å®çŸ©ãšåŠçãããå®çŸ©ãå®å šã«åãã§ãªããã°ãªããªããšããæ¡ä»¶ãæºããã
ãã®äŸã§ã¯ãç¹å¥ã«ãã1ã€ã®å°ããªæãè¿œå ããŸãããããã¯ãã¡ãœãããããããã£ãããã³ãã£ãŒã«ãã®åŠçæžã¿ä¿®é£Ÿåã§ãã
ã¡ãœãããããããã£ãããã³ãã£ãŒã«ãããåŠçããã修食åãæã€ã¯ã©ã¹ã§ã®ã¿äœ¿çšå¯èœã§ããã眲åãæ¯èŒãããšãã«ç¡èŠããããã®ãšããŠããŒã¯ããŸããããã¯ã¢ã¹ãã¯ãéçºè ã®äŸ¿å®ã®ããã«è¡ãããäžèŠãªã³ãŒãã®åé·æ§ãäœæããªãããã«ãäžè¬çãªããžãã¯ãåå¥ã®ã¡ãœããã«ç§»åã§ããŸãã
ã³ã³ãã€ã©ã¯ãã®ã³ãŒããã³ã³ãã€ã«ãããã¹ãŠãåé¡ãªãå Žåã¯ããã€ãã³ãŒãã䜿çšããŠããã»ã¹ãç¶è¡ããŸããã
ãã®äŸã§ã¯ããã€ãã®åçŽåããããå®éã«ã¯ããžãã¯ãããè€éã«ãªãå¯èœæ§ãããããšã¯æããã§ãïŒããšãã°ã1ã€ã®ã¯ã©ã¹ã«å ã®ã¯ã©ã¹ãšéšåçãªã¯ã©ã¹ã®äž¡æ¹ãå«ããå ŽåïŒããããã¯å æã§ããªãè€éãã§ã¯ãããŸããã
å®ç§ãªäžçã§ã®åºæ¬çãªIDEæ©èœ
IDEã§ã®.processed.csãã¡ã€ã«ã®ãœãŒã¹ã³ãŒãã®æäœã®ãµããŒãã¯ãäž»ã«ãå ã®/åŠçãããã¯ã©ã¹ãšã¹ããããã€ã¹ãããã®ãããã°äžã®é·ç§»ãšã®éã®æ£ããããã²ãŒã·ã§ã³ã«ãããŸãã
IDEã®2çªç®ã«éèŠãªæ©èœïŒç§ã®èŠ³ç¹ããïŒã¯ãåŠçãããã¯ã©ã¹ã®ã³ãŒãã®èªã¿åããæ¯æŽããããšã§ãã Processedã¯ã©ã¹ã«ã¯ãããã€ãã®åŽé¢ã«ãã£ãŠè¿œå ãããå€ãã®ã³ãŒããå«ããããšãã§ããŸããã°ã©ãã£ãã¯ãšãã£ã¿ã®ã¬ã€ã€ãŒã®æŠå¿µã«äŒŒããã£ã¹ãã¬ã€ã®å®è£ ã¯ããã®ç®æšãéæããããã®æã䟿å©ãªãªãã·ã§ã³ã®ããã«æãããŸããç§ãã¡ã®çŸåšã®ãã©ã°ã€ã³ã¯åæ§ã®ãã®ãå®è£ ããŠããããã®ãŠãŒã¶ãŒããã®åå¿ã¯éåžžã«è¯å®çã§ãã
AOPãæ¥åžžç掻ã«å°å ¥ããã®ã«åœ¹ç«ã€ãã1ã€ã®æ©èœã¯ããªãã¡ã¯ã¿ãªã³ã°æ©èœã§ãããŠãŒã¶ãŒãã³ãŒãã®äžéšã匷調衚瀺ãããšããExtract To AOP Templateããšèšãããšãã§ããIDEã¯æ£ãããã¡ã€ã«ãäœæããåæã³ãŒããçæãããããžã§ã¯ãã³ãŒããåæããåŸãä»ã®ã¯ã©ã¹ã®ãã³ãã¬ãŒãã䜿çšããåè£ãææ¡ããŸããã
ããŠãã±ãŒãã®äžã®ã¢ã€ã·ã³ã°ã¯ãã¢ã¹ãã¯ããã³ãã¬ãŒããäœæããããã®ãµããŒãã«ãªããŸããããšãã°ãéžæããã¯ã©ã¹/ã¡ãœããã«ã¢ã¹ãã¯ããã€ã³ã¿ã©ã¯ãã£ãã«é©çšããŠãæ瀺çãªã³ã³ãã€ã«ãµã€ã¯ã«ãªãã§ããã®å Žã§æçµçµæãè©äŸ¡ã§ããŸãã
ãªã·ã£ãŒããŒã®äœæè ãäºæ¥ãåŒãç¶ããšãéæ³ãä¿èšŒããããšç¢ºä¿¡ããŠããŸãã
å®ç§ãªäžçã§ã¢ã¹ãã¯ãã³ãŒããæžã
TRIZãèšãæãããšãã¢ã¹ãã¯ããå®è£ ããããã®çæ³çãªã³ãŒãã®èšè¿°ã¯ãã€ã³ã¹ãã«ã¡ã³ããŒã·ã§ã³ããã»ã¹ããµããŒãããããã ãã«ååšããè¿œå ã®ã³ãŒããèšè¿°ããªãããšã§ãã
çæ³çãªäžçã§ã¯ããã®ç®æšãéæããããã®ãã«ããŒããžãã¯ãäœæããæéããããã«ãã¢ã¹ãã¯ãèªäœãã³ãŒãã£ã³ã°ããããšèããŠããŸãããããŠããã®ã³ãŒãã¯ãããžã§ã¯ãèªäœã®äžå¯æ¬ ãªéšåã«ãªããŸãã
2çªç®ã®èŠæã¯ãã€ã³ã¿ã©ã¯ãã£ããªãã©ã°ã¢ã³ããã¬ã€ãå®çŸã§ããããšã§ãããã³ãã¬ãŒããäœæãããããããå€æã«äœ¿çšããããã«è¿œå ã®æé ãå®è¡ããå¿ èŠã¯ãããŸãããããŒã«ãåã³ã³ãã€ã«ãããããšã©ãŒããã£ãããããããå¿ èŠã¯ãããŸããã§ããããŸããã³ã³ãã€ã«åŸã®ãããžã§ã¯ãã§ãªãã·ã§ã³ãæ§æããŸãã
ãã³ãã¬ãŒããäœæããŠæ°è¡æžããšãããã«çµæã衚瀺ãããŸãããšã©ãŒãå«ãŸããŠããå Žåããããã®æ€åºãšãããã°ã¯ãã³ãã¬ãŒããé©çšããããã»ã¹ã«çµ±åãããããã°ã©ããŒã®è¿œå ã®äœæ¥ãå¿ èŠãšããå¥åã®éšåã§ã¯ãããŸããã
ãã³ãã¬ãŒãã®æ§æãCïŒèšèªã®æ§æã«ã§ããã ãè¿ããªãããã«ãçæ³çã«ã¯ãã€ããŒãªã¢ããªã³ã«å ããŠãããã€ãã®ããŒã¯ãŒããšãã¬ãŒã¹ãã«ããŒãè¿œå ããŸãã
çŸåšã®å®è£
æ®å¿µãªãããç§ãã¡ã¯çæ³çãªäžçã«äœãã§ããªãã®ã§ãèªè»¢è»ãåçºæããŠä¹ãå¿ èŠããããŸãã
ã³ãŒãã€ã³ãžã§ã¯ã·ã§ã³ãã³ã³ãã€ã«ããããã°
çŸåšã®ã¢ãã«ã¯ããããžã§ã¯ãã®2ã€ã®ã³ããŒãäœæããããšã§ãã1ã€ã¯ãªãªãžãã«ã®ãã®ã§ãããã°ã©ããŒã䜿çšãããã®ããã1ã€ã¯å€æããããã®ã§ãã³ã³ãã€ã«ãšå®è¡ã«äœ¿çšãããŸãã
ã·ããªãªã¯ãããªæã
- , , ..
- , , , .
- , , , WPF , ..
ãããã°ã®ããã«ãIDEã®2çªç®ã®ã³ããŒãèµ·åããããããžã§ã¯ãã®åœå¥ã®ã³ããŒãéãããå€æãé©çšãããã³ããŒã§æ©èœããŸãã
ãã®ããã»ã¹ã«ã¯ç¹å®ã®èŠåŸãå¿ èŠã§ãããæã ãããç¿æ £ã«ãªããå Žåã«ãã£ãŠã¯ãã®ã¢ãããŒãã«ã¯ããã€ãã®å©ç¹ããããŸãïŒããšãã°ãããŒã«ã«ãã·ã³ã§äœæ¥ãã代ããã«ããã«ããèµ·åããŠãªã¢ãŒããµãŒããŒã«ãããã€ã§ããŸãïŒãããã«ãVisualStudioã®ãã©ã°ã€ã³ã®ãã«ãã«ãããããã»ã¹ãç°¡çŽ åãããŸãã
IDE
ç¹å®ã®ã¿ã¹ã¯ãšããã»ã¹ã«åãããŠèª¿æŽããããã©ã°ã€ã³ã䜿çšããŠããããœãŒã¹ã³ãŒãã®å®è£ ã®ãµããŒãã¯ãã®æ©èœã®ããªãå°ããªéšåã§ãã
ããšãã°ãã°ã©ãã£ã«ã«ãšãã£ã¿ã«äŒŒãã¹ã¿ã€ã«ã§ã¬ã€ã€ãŒã衚瀺ããæ©èœã䜿çšãããšãããšãã°ãã¹ã³ãŒãïŒããšãã°ããããªãã¯ã¡ãœããã®ã¿ã衚瀺ãããããã«ããïŒãé åããšã«ã³ã¡ã³ãã¬ã€ã€ãŒãé衚瀺/衚瀺ã§ããŸããåã蟌ãŸããã³ãŒãã¯ç¹å¥ãªåœ¢åŒã®ã³ã¡ã³ãã§å²ãŸããŠãããå¥ã®ã¬ã€ã€ãŒãšããŠé衚瀺ã«ããããšãã§ããŸãã
å¥ã®å¯èœæ§ã¯ãå ã®ãã¡ã€ã«ãšå€æããããã¡ã€ã«ã®éãã瀺ãããšã§ãã IDEã¯ãããžã§ã¯ãå ã®ãã¡ã€ã«ã®ã³ããŒã®çžå¯Ÿçãªå Žæãç¥ã£ãŠãããããå ã®ãã¡ã€ã«ãšåœã§çæããããã¡ã€ã«ã®éãã衚瀺ã§ããŸãã
ãŸãããã©ã°ã€ã³ã¯ãåœã§çæãããã³ããŒã«å€æŽãå ããããšãããšèŠåããŸãïŒãã®åŸã®åå€æäžã«ãããã倱ãããªãããã«ããããïŒ
æ§æ
å¥ã®ã¿ã¹ã¯ã¯ãå€æã«ãŒã«ãèšå®ããããšã§ããã©ã®ã¯ã©ã¹ãšã¡ãœããã«å€æãé©çšããŸããã
ããã€ãã®ã¬ãã«ã䜿çšããŸãã
æåã®ã¬ãã«ã¯ãæäžäœã®æ§æãã¡ã€ã«ã§ãããã¡ã€ã«ã·ã¹ãã ã®ãã¹ããã¡ã€ã«åã®ãã¿ãŒã³ãã¯ã©ã¹ãŸãã¯ã¡ãœãããã¯ã©ã¹ã®ã¹ã³ãŒããã¡ãœãããŸãã¯ããããã£ã«å¿ããŠã«ãŒã«ãèšå®ã§ããŸãã
2çªç®ã®ã¬ãã«ã¯ãã¯ã©ã¹ãã¡ãœããããŸãã¯ãã£ãŒã«ãã®å±æ§ã®ã¬ãã«ã§ã®å€æã«ãŒã«ã®é©çšã瀺ããŸãã
ã³ãŒããããã¯ã®ã¬ãã«ã§ã®3çªç®ãš4çªç®ã¯ããœãŒã¹ã³ãŒãã®ç¹å®ã®å Žæã§ã®ãã³ãã¬ãŒãã®å€æã®çµæãå«ããããšãæ瀺çã«ç€ºããŠããŸãã
ãã³ãã¬ãŒã
æŽå²çã«ãèªåçæã®ç®çã§ãT4圢åŒã®ãã³ãã¬ãŒãã䜿çšãããããå€æçšã®ãã³ãã¬ãŒããšåãã¢ãããŒãã䜿çšããããšã¯éåžžã«è«ççã§ãããT4ãã³ãã¬ãŒãã«ã¯ãä»»æã®CïŒã³ãŒããå®è¡ããæ©èœãå«ãŸããŠããããªãŒããŒããããæå°éã§ãè¡šçŸåãåªããŠããŸãã
T4ã䜿çšããããšããªã人ã«ãšã£ãŠãæãç°¡åãªé¡äŒŒç¹ã¯ASPX圢åŒãæ瀺ããããšã§ããããã¯ãHTMLã®ä»£ããã«CïŒã§ãœãŒã¹ã³ãŒããçæããIISã§ã¯ãªããçµæãã³ã³ãœãŒã«ïŒãŸãã¯ãã¡ã€ã«ïŒã«åºåããå¥åã®ãŠãŒãã£ãªãã£ãšããŠå®è¡ãããŸãã
ã®äŸ
ãããå®éã«ã©ã®ããã«æ©èœããããç解ããããã«ãæãç°¡åãªããšã¯ãå€æã®ååŸã®ã³ãŒããšãå€æäžã«äœ¿çšããããã³ãã¬ãŒãã®ãœãŒã¹ã³ãŒãã瀺ãããšã§ããæãç°¡åãªãªãã·ã§ã³ã瀺ããŸãããå¯èœæ§ã¯ããªãã®æ³ååã«ãã£ãŠã®ã¿å¶éãããŸãã
å€æåã®ãµã³ãã«ãœãŒã¹ã³ãŒã
// ##aspect=AutoComment
using AOP.Common;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Aspectimum.Demo.Lib
{
[AopTemplate("ClassLevelTemplateForMethods", NameFilter = "First")]
[AopTemplate("StaticAnalyzer", Action = AopTemplateAction.Classes)]
[AopTemplate("DependencyInjection", AdvicePriority = 500, Action = AopTemplateAction.PostProcessingClasses)]
[AopTemplate("ResourceReplacer", AdvicePriority = 1000, ExtraTag = "ResourceFile=Demo.resx,ResourceClass=Demo", Action = AopTemplateAction.PostProcessingClasses)]
public class ConsoleDemo
{
public virtual Person FirstDemo(string firstName, string lastName, int age)
{
Console.Out.WriteLine("FirstDemo: 1");
// ##aspect="FirstDemoComment" extra data here
return new Person()
{
FirstName = firstName,
LastName = lastName,
Age = age,
};
}
private static IConfigurationRoot _configuration = inject;
private IDataService _service { get; } = inject;
private Person _somePerson = inject;
[AopTemplate("LogExceptionMethod")]
[AopTemplate("StopWatchMethod")]
[AopTemplate("MethodFinallyDemo", AdvicePriority = 100)]
public Customer[] SecondDemo(Person[] people)
{
IEnumerable<Customer> Customers;
Console.Out.WriteLine("SecondDemo: 1");
Console.Out.WriteLine(i18("SecondDemo: i18"));
int configDelayMS = inject;
string configServerName = inject;
using (new AopTemplate("SecondDemoUsing", extraTag: "test extra"))
{
Customers = people.Select(s => new Customer()
{
FirstName = s.FirstName,
LastName = s.LastName,
Age = s.Age,
Id = s.Id
});
_service.Init(Customers);
foreach (var customer in Customers)
{
Console.Out.WriteLine(i18($"First Name {customer.FirstName} Last Name {customer.LastName}"));
Console.Out.WriteLine("SecondDemo: 2 " + i18("First Name ") + customer.FirstName + i18(" Last Name ") + customer.LastName);
}
}
Console.Out.WriteLine(i18(String.Format("SecondDemo: {0}", "3")));
Console.Out.WriteLine($"Server {configServerName} default delay {configDelayMS}");
Console.Out.WriteLine($"Customer for ID=5 is {_service.GetCustomerName(5)}");
return Customers.ToArray();
}
protected static string i18(string s) => s;
protected static dynamic inject;
[AopTemplate("NotifyPropertyChangedClass", Action = AopTemplateAction.Classes)]
[AopTemplate("NotifyPropertyChanged", Action = AopTemplateAction.Properties)]
public class Person
{
[AopTemplate("CacheProperty", extraTag: "{ \"CacheKey\": \"name_of_cache_key\", \"ExpiresInMinutes\": 10 }")]
public string FullName
{
get
{
// ##aspect="FullNameComment" extra data here
return $"{FirstName} {LastName}";
}
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
[AopTemplate("NotifyPropertyChanged", Action = AopTemplateAction.Properties)]
public class Customer : Person
{
public double CreditScore { get; set; }
}
public interface IDataService
{
void Init(IEnumerable<Customer> customers);
string GetCustomerName(int customerId);
}
public class DataService: IDataService
{
private IEnumerable<Customer> _customers;
public void Init(IEnumerable<Customer> customers)
{
_customers = customers;
}
public string GetCustomerName(int customerId)
{
return _customers.FirstOrDefault(w => w.Id == customerId)?.FullName;
}
}
public class MockDataService : IDataService
{
private IEnumerable<Customer> _customers;
public void Init(IEnumerable<Customer> customers)
{
if(customers == null)
throw (new Exception("IDataService.Init(customers == null)"));
}
public string GetCustomerName(int customerId)
{
if (customerId < 0)
throw (new Exception("IDataService.GetCustomerName: customerId cannot be negative"));
if (customerId == 0)
throw (new Exception("IDataService.GetCustomerName: customerId cannot be zero"));
return $"FirstName{customerId} LastName{customerId}";
}
}
}
}
å€æåŸã®ãœãŒã¹ã³ãŒãã®ãã«ããŒãžã§ã³
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
//
// Generated base on file: ConsoleDemo.cs
// ##sha256: ekmmxFSeH5ev8Epvl7QvDL+D77DHwq1gHDnCxzeBWcw
// Created By: JohnSmith
// Created Machine: 127.0.0.1
// Created At: 2020-09-19T23:18:07.2061273-04:00
//
// </auto-generated>
//------------------------------------------------------------------------------
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
namespace Aspectimum.Demo.Lib
{
public class ConsoleDemo
{
public virtual Person FirstDemo(string firstName, string lastName, int age)
{
Console.Out.WriteLine("FirstDemo: 1");
// FirstDemoComment replacement extra data here
return new Person()
{FirstName = firstName, LastName = lastName, Age = age, };
}
private static IConfigurationRoot _configuration = new ConfigurationBuilder()
.SetBasePath(System.IO.Path.Combine(AppContext.BaseDirectory))
.AddJsonFile("appsettings.json", optional: true)
.Build();
private IDataService _service { get; } = new DataService();
#error Cannot find injection rule for Person _somePerson
private Person _somePerson = inject;
public Customer[] SecondDemo(Person[] people)
{
try
{
#error variable "Customers" doesn't match code standard rules
IEnumerable<Customer> Customers;
Console.Out.WriteLine("SecondDemo: 1");
#error Cannot find resource for a string "SecondDemo: i18", please add it to resources
Console.Out.WriteLine(i18("SecondDemo: i18"));
int configDelayMS = Int32.Parse(_configuration["delay_ms"]);
string configServerName = _configuration["server_name"];
{
// second demo test extra
{
Customers = people.Select(s => new Customer()
{FirstName = s.FirstName, LastName = s.LastName, Age = s.Age, Id = s.Id});
_service.Init(Customers);
foreach (var customer in Customers)
{
Console.Out.WriteLine(String.Format(Demo.First_Last_Names_Formatted, customer.FirstName, customer.LastName));
Console.Out.WriteLine("SecondDemo: 2 " + (Demo.First_Name + " ") + customer.FirstName + (" " + Demo.Last_Name + " ") + customer.LastName);
}
}
}
#error Argument for i18 method must be either string literal or interpolated string, but instead got Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax
#warning Please replace String.Format with string interpolation format.
Console.Out.WriteLine(i18(String.Format("SecondDemo: {0}", "3")));
Console.Out.WriteLine($"Server {configServerName} default delay {configDelayMS}");
Console.Out.WriteLine($"Customer for ID=5 is {_service.GetCustomerName(5)}");
return Customers.ToArray();
}
catch (Exception logExpn)
{
Console.Error.WriteLine($"Exception in SecondDemo\r\n{logExpn.Message}\r\n{logExpn.StackTrace}");
throw;
}
}
protected static string i18(string s) => s;
protected static dynamic inject;
public class Person : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
public string FullName
{
get
{
System.Runtime.Caching.ObjectCache cache = System.Runtime.Caching.MemoryCache.Default;
string cachedData = cache["name_of_cache_key"] as string;
if (cachedData == null)
{
cachedData = GetPropertyData();
if (cachedData != null)
{
cache.Set("name_of_cache_key", cachedData, System.DateTimeOffset.Now.AddMinutes(10));
}
}
return cachedData;
string GetPropertyData()
{
// FullNameComment FullName
return $"{FirstName} {LastName}";
}
}
}
private int _id;
public int Id
{
get
{
return _id;
}
set
{
if (_id != value)
{
_id = value;
NotifyPropertyChanged();
}
}
}
private string _firstName;
public string FirstName
{
get
{
return _firstName;
}
set
{
if (_firstName != value)
{
_firstName = value;
NotifyPropertyChanged();
}
}
}
private string _lastName;
public string LastName
{
get
{
return _lastName;
}
set
{
if (_lastName != value)
{
_lastName = value;
NotifyPropertyChanged();
}
}
}
private int _age;
public int Age
{
get
{
return _age;
}
set
{
if (_age != value)
{
_age = value;
NotifyPropertyChanged();
}
}
}
}
public class Customer : Person
{
private double _creditScore;
public double CreditScore
{
get
{
return _creditScore;
}
set
{
if (_creditScore != value)
{
_creditScore = value;
NotifyPropertyChanged();
}
}
}
}
public interface IDataService
{
void Init(IEnumerable<Customer> customers);
string GetCustomerName(int customerId);
}
public class DataService : IDataService
{
private IEnumerable<Customer> _customers;
public void Init(IEnumerable<Customer> customers)
{
_customers = customers;
}
public string GetCustomerName(int customerId)
{
return _customers.FirstOrDefault(w => w.Id == customerId)?.FullName;
}
}
public class MockDataService : IDataService
{
private IEnumerable<Customer> _customers;
public void Init(IEnumerable<Customer> customers)
{
if (customers == null)
throw (new Exception("IDataService.Init(customers == null)"));
}
public string GetCustomerName(int customerId)
{
if (customerId < 0)
throw (new Exception("IDataService.GetCustomerName: customerId cannot be negative"));
if (customerId == 0)
throw (new Exception("IDataService.GetCustomerName: customerId cannot be zero"));
return $"FirstName{customerId} LastName{customerId}";
}
}
}
}
// ##template=AutoComment sha256=Qz6vshTZl2/u+NgtcV4u5W5RZMb9JPkJ2Zj0yvQBH9w
// ##template=AopCsharp.ttinclude sha256=2QR7LE4yvfWYNl+JVKQzvEBwcWvReeupVpslWTSWQ0c
// ##template=FirstDemoComment sha256=eIleHCim5r9F/33Mv9B7pcNQ/dlfEhDVXJVhA7+3OgY
// ##template=FullNameComment sha256=2/Ipn8fk2y+o/FVQHAWnrOlhqS5ka204YctZkwl/CUs
// ##template=NotifyPropertyChangedClass sha256=sxRrSjUSrynQSPjo85tmQywQ7K4fXFR7nN2mX87fCnk
// ##template=StaticAnalyzer sha256=zmJsj/FWmjqDDnpZXhoAxQB61nYujd41ILaQ4whcHyY
// ##template=LogExceptionMethod sha256=+zTre3r3LR9dm+bLPEEXg6u2OtjFg+/V6aCnJKijfcg
// ##template=NotifyPropertyChanged sha256=PMgorLSwEChpIPnEWXfEuUzUm4GO/6pMmoJdF7qcgn8
// ##template=CacheProperty sha256=oktDGTfC2hHoqpbKkeNABQaPdq6SrVLRFEQdNMoY4zE
// ##template=DependencyInjection sha256=nPq/ZxVBpgrDzyH+uLtJvD1aKbajKinX/DUBQ4BGG9g
// ##template=ResourceReplacer sha256=ZyUljjKKj0jLlM2nUIr1oJc1L7otYUI8WqWN7um6NxI
説æãšãã³ãã¬ãŒãã³ãŒã
èªåã³ã¡ã³ããã³ãã¬ãŒã
// ##aspect=AutoComment
ãœãŒã¹ã³ãŒãã§ç¹å¥ãªåœ¢åŒã®ã³ã¡ã³ããèŠã€ãã£ãå Žåã¯ãæå®ããããã³ãã¬ãŒãïŒãã®å Žåã¯AutoCommentïŒãå®è¡ãããã®ã³ã¡ã³ãã®ä»£ããã«å€æçµæãæ¿å ¥ããŸãããã®äŸã§ã¯ããã®ãã¡ã€ã«ã®ã³ãŒããå€æã®çµæã§ããããã®ãã¡ã€ã«ãçŽæ¥å€æŽããããšã¯æå³ããªãããšãããã°ã©ããŒã«èŠåããç¹å¥ãªå 責äºé ãèªåçã«æ¿å ¥ããããšã¯çã«ããªã£ãŠããŸãã
ãã³ãã¬ãŒãã³ãŒãAutoComment.t4
<#@ include file="AopCsharp.ttinclude" #>
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
//
// Generated base on file: <#= FileName #>
// ##sha256: <#= FileSha256 #>
// Created By: <#= User #>
// Created Machine: <#= MachineName #>
// Created At: <#= Now #>
//
// </auto-generated>
//------------------------------------------------------------------------------
å€æ°FileNameãFileSha256ãUserãMachineNameãããã³Nowã¯ãå€æããã»ã¹ãããã³ãã¬ãŒãã«ãšã¯ã¹ããŒããããŸãã
å€æçµæ
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
//
// Generated base on file: ConsoleDemo.cs
// ##sha256: PV3lHNDftTzVYnzNCZbKvtHCbscT0uIcHGRR/NJFx20
// Created By: EuGenie
// Created Machine: 192.168.0.1
// Created At: 2017-12-09T14:49:26.7173975-05:00
//
// </auto-generated>
//------------------------------------------------------------------------------
次ã®å€æã¯ãã¯ã©ã¹ã®å±æ§ãšããŠæå®ãããŸã
[AopTemplateïŒ "ClassLevelTemplateForMethods"ãNameFilter = "First"ïŒ]
ãã®å±æ§ã¯ããã³ãã¬ãŒãããFirstããšããåèªãå«ããã¹ãŠã®ã¯ã©ã¹ã¡ãœããã«é©çšããå¿ èŠãããããšã瀺ããŸããNameFilterãã©ã¡ãŒã¿ãŒã¯ãå€æã«å«ããã¡ãœããã決å®ããããã«äœ¿çšãããéåžžã®åŒãã¿ãŒã³ã§ãã
ãã³ãã¬ãŒãã³ãŒãClassLevelTemplateForMethods.t4
<#@ include file="AopCsharp.ttinclude" #>
// class level template
<#= MethodStart() #><#= MethodBody() #><#= MethodEnd() #>
ããã¯ã
// class level template
ã¡ãœããã³ãŒãã®
å€æçµæã®åã«ã³ã¡ã³ããè¿œå ããæãç°¡åãªäŸã§ãã
// class level template
public virtual Person FirstDemo(string firstName, string lastName, int age)
{
Console.Out.WriteLine("FirstDemo: 1");
// ##aspect="FirstDemoComment" extra data here
return new Person()
{
FirstName = firstName,
LastName = lastName,
Age = age,
};
}
次ã®å€æã¯ãåãã¡ãœããã«é©çšãããè€æ°ã®å€æã瀺ãããã«ãã¡ãœããå±æ§ãšããŠæå®ãããŠããŸãã LogExceptionMethod.t4ãã³ãã¬ãŒã
[AopTemplate("LogExceptionMethod")]
[AopTemplate("StopWatchMethod")]
[AopTemplate("MethodFinallyDemo", AdvicePriority = 100)]
<#@ include file="AopCsharp.ttinclude" #>
<# EnsureUsing("System"); #>
<#= MethodStart() #>
try
{
<#= MethodBody() #>
}
catch(Exception logExpn)
{
Console.Error.WriteLine($"Exception in <#= MethodName #>\r\n{logExpn.Message}\r\n{logExpn.StackTrace}");
throw;
}
<#= MethodEnd() #>
StopWatchMethod.t4ãã³ãã¬ãŒã
<#@ include file="AopCsharp.ttinclude" #>
<# EnsureUsing("System.Diagnostics"); #>
<#= MethodStart() #>
var stopwatch = Stopwatch.StartNew();
try
{
<#= MethodBody() #>
}
finally
{
stopwatch.Stop();
Console.Out.WriteLine($"Method <#= MethodName #>: {stopwatch.ElapsedMilliseconds}");
}
<#= MethodEnd() #>
MethodFinallyDemo.t4ãã³ãã¬ãŒã
<#@ include file="AopCsharp.ttinclude" #>
<#= MethodStart() #>
try
{
<#= MethodBody() #>
}
finally
{
// whatever logic you need to include for a method
}
<#= MethodEnd() #>
å€æã®çµæ
public Customer[] SecondDemo(Person[] people)
{
try
{
var stopwatch = Stopwatch.StartNew();
try
{
try
{
IEnumerable<Customer> customers;
Console.Out.WriteLine("SecondDemo: 1");
{
// second demo test extra
{
customers = people.Select(s => new Customer()
{FirstName = s.FirstName, LastName = s.LastName, Age = s.Age, });
foreach (var customer in customers)
{
Console.Out.WriteLine($"SecondDemo: 2 {customer.FirstName} {customer.LastName}");
}
}
}
Console.Out.WriteLine("SecondDemo: 3");
return customers.ToArray();
}
catch (Exception logExpn)
{
Console.Error.WriteLine($"Exception in SecondDemo\r\n{logExpn.Message}\r\n{logExpn.StackTrace}");
throw;
}
}
finally
{
stopwatch.Stop();
Console.Out.WriteLine($"Method SecondDemo: {stopwatch.ElapsedMilliseconds}");
}
}
finally
{
// whatever logic you need to include for a method
}
}
次ã®å€æã¯ãusingã³ã³ã¹ãã©ã¯ãã«éå®ããããããã¯ã«å¯ŸããŠè¡ãããŸãã
using (new AopTemplate("SecondDemoUsing", extraTag: "test extra"))
{
customers = people.Select(s => new Customer()
{
FirstName = s.FirstName,
LastName = s.LastName,
Age = s.Age,
});
foreach (var customer in customers)
{
Console.Out.WriteLine($"SecondDemo: 2 {customer.FirstName} {customer.LastName}");
}
}
SecondDemoUsing.t4ãã³ãã¬ãŒã
<#@ include file="AopCsharp.ttinclude" #>
// second demo <#= ExtraTag #>
<#= StatementBody() #>
ExtraTagã¯ããã©ã¡ãŒã¿ãŒãšããŠæž¡ãããæååã§ããããã¯ãå ¥åãã©ã¡ãŒã¿ã«å¿ããŠåäœããããã«ç°ãªãå¯èœæ§ããããžã§ããªãã¯ã«åœ¹ç«ã¡ãŸãã
å€æçµæ
{
// second demo test extra
{
customers = people.Select(s => new Customer()
{FirstName = s.FirstName, LastName = s.LastName, Age = s.Age, });
foreach (var customer in customers)
{
Console.Out.WriteLine($"SecondDemo: 2 {customer.FirstName} {customer.LastName}");
}
}
}
次ã®å€æã¯ã NotifyPropertyChangedã¯ã©ã¹ã®å±æ§ã«ãã£ãŠæå®ãããŸããããã¯å€å žçãªäŸã§ããããã°ã®äŸãšãšãã«ãã¢ã¹ãã¯ãæåããã°ã©ãã³ã°ã®ã»ãšãã©ã®äŸã§ç€ºãããŠããŸãã
[AopTemplate("NotifyPropertyChangedClass", Action = AopTemplaceAction.Classes)]
[AopTemplate("NotifyPropertyChanged", Action = AopTemplaceAction.Properties)]
ã¯ã©ã¹ã³ãŒãã«é©çšãããNotifyPropertyChangedClass.t4ãã³ãã¬ãŒã
.
, AOP .Net
<#@ include file="AopCsharp.ttinclude" #>
<#
// the class already implements INotifyPropertyChanged, nothing to do here
if(ImplementsBaseType(ClassNode, "INotifyPropertyChanged", "System.ComponentModel.INotifyPropertyChanged"))
return null;
var classNode = AddBaseTypes<ClassDeclarationSyntax>(ClassNode, "System.ComponentModel.INotifyPropertyChanged");
#>
<#= ClassStart(classNode) #>
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
<#= ClassBody(classNode) #>
<#= ClassEnd(classNode) #>
.
Fogy
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
public partial class ModuleWeaver
{
public void InjectINotifyPropertyChangedInterface(TypeDefinition targetType)
{
targetType.Interfaces.Add(new InterfaceImplementation(PropChangedInterfaceReference));
WeaveEvent(targetType);
}
void WeaveEvent(TypeDefinition type)
{
var propertyChangedFieldDef = new FieldDefinition("PropertyChanged", FieldAttributes.Private | FieldAttributes.NotSerialized, PropChangedHandlerReference);
type.Fields.Add(propertyChangedFieldDef);
var propertyChangedField = propertyChangedFieldDef.GetGeneric();
var eventDefinition = new EventDefinition("PropertyChanged", EventAttributes.None, PropChangedHandlerReference)
{
AddMethod = CreateEventMethod("add_PropertyChanged", DelegateCombineMethodRef, propertyChangedField),
RemoveMethod = CreateEventMethod("remove_PropertyChanged", DelegateRemoveMethodRef, propertyChangedField)
};
type.Methods.Add(eventDefinition.AddMethod);
type.Methods.Add(eventDefinition.RemoveMethod);
type.Events.Add(eventDefinition);
}
MethodDefinition CreateEventMethod(string methodName, MethodReference delegateMethodReference, FieldReference propertyChangedField)
{
const MethodAttributes Attributes = MethodAttributes.Public |
MethodAttributes.HideBySig |
MethodAttributes.Final |
MethodAttributes.SpecialName |
MethodAttributes.NewSlot |
MethodAttributes.Virtual;
var method = new MethodDefinition(methodName, Attributes, TypeSystem.VoidReference);
method.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, PropChangedHandlerReference));
var handlerVariable0 = new VariableDefinition(PropChangedHandlerReference);
method.Body.Variables.Add(handlerVariable0);
var handlerVariable1 = new VariableDefinition(PropChangedHandlerReference);
method.Body.Variables.Add(handlerVariable1);
var handlerVariable2 = new VariableDefinition(PropChangedHandlerReference);
method.Body.Variables.Add(handlerVariable2);
var loopBegin = Instruction.Create(OpCodes.Ldloc, handlerVariable0);
method.Body.Instructions.Append(
Instruction.Create(OpCodes.Ldarg_0),
Instruction.Create(OpCodes.Ldfld, propertyChangedField),
Instruction.Create(OpCodes.Stloc, handlerVariable0),
loopBegin,
Instruction.Create(OpCodes.Stloc, handlerVariable1),
Instruction.Create(OpCodes.Ldloc, handlerVariable1),
Instruction.Create(OpCodes.Ldarg_1),
Instruction.Create(OpCodes.Call, delegateMethodReference),
Instruction.Create(OpCodes.Castclass, PropChangedHandlerReference),
Instruction.Create(OpCodes.Stloc, handlerVariable2),
Instruction.Create(OpCodes.Ldarg_0),
Instruction.Create(OpCodes.Ldflda, propertyChangedField),
Instruction.Create(OpCodes.Ldloc, handlerVariable2),
Instruction.Create(OpCodes.Ldloc, handlerVariable1),
Instruction.Create(OpCodes.Call, InterlockedCompareExchangeForPropChangedHandler),
Instruction.Create(OpCodes.Stloc, handlerVariable0),
Instruction.Create(OpCodes.Ldloc, handlerVariable0),
Instruction.Create(OpCodes.Ldloc, handlerVariable1),
Instruction.Create(OpCodes.Bne_Un_S, loopBegin), // go to begin of loop
Instruction.Create(OpCodes.Ret));
method.Body.InitLocals = true;
method.Body.OptimizeMacros();
return method;
}
}
, AOP .Net
ã¯ã©ã¹ããããã£ã«é©çšãããNotifyPropertyChanged.t4ãã³ãã¬ãŒã
<#@ include file="AopCsharp.ttinclude" #>
<#
if(!(PropertyHasEmptyGetBlock() && PropertyHasEmptySetBlock()))
return null;
string privateUnqiueName = GetUniquePrivatePropertyName(ClassNode, PropertyNode.Identifier.ToString());
#>
private <#= PropertyNode.Type.ToFullString() #> <#= privateUnqiueName #><#= PropertyNode.Initializer != null ? " = " + PropertyNode.Initializer.ToFullString() : "" #>;
<#= PropertyNode.AttributeLists.ToFullString() + PropertyNode.Modifiers.ToFullString() + PropertyNode.Type.ToFullString() + PropertyNode.Identifier.ToFullString() #>
{
get { return <#= privateUnqiueName #>; }
set
{
if(<#= privateUnqiueName #> != value)
{
<#= privateUnqiueName #> = value;
NotifyPropertyChanged();
}
}
}
ã¯ã©ã¹ãšããããã£ã®å ã®ã³ãŒã
public class Person
{
public int Id { get; set; }
// ...
}
å€æçµæ
public class Person : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
private int _id;
public int Id
{
get
{
return _id;
}
set
{
if (_id != value)
{
_id = value;
NotifyPropertyChanged();
}
}
}
// ...
}
ããããã£ã®çµæããã£ãã·ã¥ããããã®ãã³ãã¬ãŒãã®äŸ
[AopTemplate("CacheProperty", extraTag: "{ \"CacheKey\": \"name_of_cache_key\", \"ExpiresInMinutes\": 10 }")]
ããã³ãã¬ãŒããã©ã¡ãŒã¿ã§æå®ãããJSONå±æ§ãšããŠæå®ãããŸããæ瀺çãªãã©ã¡ãŒã¿ãŒããªãå Žåã¯ãããã©ã«ãã®ãã©ã¡ãŒã¿ãŒã䜿çšãããŸãã
CacheProperty.t4ãã³ãã¬ãŒã
<#@ include file="AopCsharp.ttinclude" #>
<#
// The template accepts a configuration value from extraTag in two ways
// 1. as a number of minutes to use for expiration (example: 8)
// 2. as a string in JSON in format { CacheKey: "name_of_cache_key", CacheKeyVariable: "name_of_variable", ExpiresInMinutes: 10, ExpiresVariable: "name_of_variable" }
//
// CacheKey (optional) name of the cache key, the name will be used as a literal string (example: my_key)
// CacheKeyVariable (optional) name of variable that holds the cache key (example: GlobalConsts.MyKeyName)
//
// ExpiresInMinutes (optional) number minutes that the cache value will expires (example: 12)
// ExpiresVariable (optional) name of a variable that the expiration value will be get from (example: AppConfig.EXPIRE_CACHE)
//
// if any of expiration values are not specified, 5 minutes default expiration will be used
if(!PropertyHasAnyGetBlock())
return null;
const int DEFAULT_EXPIRES_IN_MINUTES = 5;
string propertyName = PropertyNode.Identifier.ToFullString().Trim();
string propertyType = PropertyNode.Type.ToFullString().Trim();
string expiresInMinutes = DEFAULT_EXPIRES_IN_MINUTES.ToString();
string cacheKey = "\"" + ClassNode.Identifier.ToFullString() + ":" + propertyName + "\"";
if(!String.IsNullOrEmpty(ExtraTag))
{
if(Int32.TryParse(ExtraTag, out int exp))
{
expiresInMinutes = exp.ToString();
}
else
{
JsonDocument json = ExtraTagAsJson();
if(json != null && json.RootElement.ValueKind == JsonValueKind.Object)
{
if(json.RootElement.TryGetProperty("CacheKey", out JsonElement cacheKeyElement))
{
string s = cacheKeyElement.GetString();
if(!String.IsNullOrEmpty(s))
cacheKey = "\"" + s + "\"";
}
else if(json.RootElement.TryGetProperty("CacheKeyVariable", out JsonElement cacheVariableElement))
{
string s = cacheVariableElement.GetString();
if(!String.IsNullOrEmpty(s))
cacheKey = s;
}
if(json.RootElement.TryGetProperty("ExpiresInMinutes", out JsonElement expiresInMinutesElement))
{
if(expiresInMinutesElement.TryGetInt32(out int v) && v > 0)
expiresInMinutes = "" + v;
}
else if(json.RootElement.TryGetProperty("ExpiresVariable", out JsonElement expiresVariableElement))
{
string s = expiresVariableElement.GetString();
if(!String.IsNullOrEmpty(s))
expiresInMinutes = s;
}
}
}
}
#>
<#= PropertyDefinition() #>
{
get
{
System.Runtime.Caching.ObjectCache cache = System.Runtime.Caching.MemoryCache.Default;
<#= propertyType #> cachedData = cache[<#= cacheKey #>] as <#= propertyType #>;
if(cachedData == null)
{
cachedData = GetPropertyData();
if(cachedData != null)
{
cache.Set(<#= cacheKey #>, cachedData, System.DateTimeOffset.Now.AddMinutes(<#= expiresInMinutes #>));
}
}
return cachedData;
<#= propertyType #> GetPropertyData()
{
<# if(PropertyNode.ExpressionBody != null ) { #>
return (<#= PropertyNode.ExpressionBody.Expression.ToFullString() #>);
<# } else if(PropertyNode?.AccessorList?.Accessors.FirstOrDefault(w => w.ExpressionBody != null && w.Keyword.ToString() == "get") != null) { #>
return (<#= PropertyNode?.AccessorList?.Accessors.FirstOrDefault(w => w.ExpressionBody != null && w.Keyword.ToString() == "get").ExpressionBody.Expression.ToFullString() #>);
<# } else { #>
<#= PropertyGetBlock() #>
<# } #>
}
}
<#
if(PropertyHasAnySetBlock()) { #>
set
{
System.Runtime.Caching.ObjectCache cache = System.Runtime.Caching.MemoryCache.Default;
cache.Remove(<#= cacheKey #>); // invalidate cache for the property
<#= PropertySetBlock() #>
}
<# } #>
}
ãœãŒã¹
[AopTemplate("CacheProperty", extraTag: "{ \"CacheKey\": \"name_of_cache_key\", \"ExpiresInMinutes\": 10 }")]
public string FullName
{
get
{
return $"{FirstName} {LastName}";
}
}
CacheProperty.t4ã®å€æçµæ
public string FullName
{
get
{
System.Runtime.Caching.ObjectCache cache = System.Runtime.Caching.MemoryCache.Default;
string cachedData = cache["name_of_cache_key"] as string;
if (cachedData == null)
{
cachedData = GetPropertyData();
if (cachedData != null)
{
cache.Set("name_of_cache_key", cachedData, System.DateTimeOffset.Now.AddMinutes(10));
}
}
return cachedData;
string GetPropertyData()
{
// FullNameComment FullName
return $"{FirstName} {LastName}";
}
}
}
ã³ã¡ã³ãããã®ãã³ãã¬ãŒããžã®æ¬¡ã®åŒã³åºã
// ##aspect="FullNameComment" extra data here
FullNameComment.t4ãã³ãã¬ãŒã
<#@ include file="AopCsharp.ttinclude" #>
// FullNameComment <#= PropertyNode.Identifier #>
AutoComment.t4ãã³ãã¬ãŒããšéåžžã«äŒŒãŠããŸãããããã§ã¯PropertyNodeã®äœ¿çšæ³ã瀺ããŸãããŸãããããã«ããè¿œå ããŒã¿ãããŒã¿ã¯ãExtraTagãã©ã¡ãŒã¿ãŒãä»ããŠFullNameComment.t4ãã³ãã¬ãŒãã§äœ¿çšã§ããŸãïŒãã ãããã®äŸã§ã¯äœ¿çšããªããããåã«ç¡èŠãããŸãïŒ
å€æçµæ
// FullNameComment FullName
[AopTemplate("NotifyPropertyChanged", Action = AopTemplaceAction.Properties)]
AND å±æ§ã§æå®ããããã¡ã€ã«å ã®æ¬¡ã®å€æã¯ãPersonã¯ã©ã¹ã®å€æãšåãã§ããNotifyPropertyChanged.t4ãã³ãã¬ãŒãã®ãœãŒã¹ã³ãŒãã¯ãã§ã«äžèšã«å«ãŸããŠããŸãã
å€æçµæ
public class Customer : Person
{
private double _creditScore;
public double CreditScore
{
get
{
return _creditScore;
}
set
{
if (_creditScore != value)
{
_creditScore = value;
NotifyPropertyChanged();
}
}
}
}
æåŸã®éšå
ãã®èšäºã§ã¯ã¢ã¹ãã¯ãæåã®ããã°ã©ãã³ã°ã«çŠç¹ãåœãŠãŠããŸããããœãŒã¹ã³ãŒãå€æææ³ã¯æ®éçã§ãããååãšããŠãAOPã«é¢é£ããªãã¿ã¹ã¯ã«äœ¿çšã§ããŸãã
ããšãã°ãäŸåé¢ä¿ã®æ³šå ¥ã«äœ¿çšã§ããŸãããã«ããã©ã¡ãŒã¿ã«å¿ããŠãªãœãŒã¹äœæã³ãŒããå€æŽããŸãã
DependencyInjection.t4ãã³ãã¬ãŒã
<#@ include file="AopCsharp.ttinclude" #>
<#
var syntaxNode = FieldsInjection(SyntaxNode);
syntaxNode = VariablesInjection(syntaxNode);
syntaxNode = PropertiesInjection(syntaxNode);
if(syntaxNode == SyntaxNode)
return null;
#>
<#= syntaxNode.ToFullString() #>
<#+
private SyntaxNode VariablesInjection(SyntaxNode syntaxNode)
{
return RewriteNodes<LocalDeclarationStatementSyntax >(syntaxNode, OnLocalVariablesInjection);
SyntaxNode OnLocalVariablesInjection(LocalDeclarationStatementSyntax node)
{
var errorMsgs = new System.Text.StringBuilder();
SyntaxNode syntaxNode = RewriteNodes<VariableDeclaratorSyntax>(node, (n) => OnVariableDeclaratorVisit(n, node.Declaration.Type, errorMsgs));
if(errorMsgs.Length > 0)
return AddErrorMessageTrivia(syntaxNode, errorMsgs.ToString());
return syntaxNode;
}
}
private SyntaxNode PropertiesInjection(SyntaxNode syntaxNode)
{
return RewriteNodes<PropertyDeclarationSyntax>(syntaxNode, OnPropertyInjection);
SyntaxNode OnPropertyInjection(PropertyDeclarationSyntax node)
{
if(node.Initializer?.Value?.ToString() != "inject")
return node;
var errorMsgs = new System.Text.StringBuilder();
SyntaxNode syntaxNode = DoInjection(node, node.Identifier.ToString().Trim(), node.Initializer.Value, node.Type, errorMsgs);
if(errorMsgs.Length > 0)
return AddErrorMessageTrivia(syntaxNode, errorMsgs.ToString());
return syntaxNode;
}
}
private SyntaxNode FieldsInjection(SyntaxNode syntaxNode)
{
return RewriteNodes<BaseFieldDeclarationSyntax>(syntaxNode, OnFieldsInjection);
SyntaxNode OnFieldsInjection(BaseFieldDeclarationSyntax node)
{
var errorMsgs = new System.Text.StringBuilder();
SyntaxNode syntaxNode = RewriteNodes<VariableDeclaratorSyntax>(node, (n) => OnVariableDeclaratorVisit(n, node.Declaration.Type, errorMsgs));
if(errorMsgs.Length > 0)
return AddErrorMessageTrivia(syntaxNode, errorMsgs.ToString());
return syntaxNode;
}
}
private SyntaxNode OnVariableDeclaratorVisit(VariableDeclaratorSyntax node, TypeSyntax typeSyntax, System.Text.StringBuilder errorMsgs)
{
if(node.Initializer?.Value?.ToString() != "inject")
return node;
return DoInjection(node, node.Identifier.ToString().Trim(), node.Initializer.Value, typeSyntax, errorMsgs);
}
private SyntaxNode DoInjection(SyntaxNode node, string varName, ExpressionSyntax initializerNode, TypeSyntax typeSyntax, System.Text.StringBuilder errorMsgs)
{
string varType = typeSyntax.ToString().Trim();
Log($"{varName} {varType} {initializerNode.ToString()}");
if(varName.StartsWith("config"))
{
string configName = Regex.Replace(Regex.Replace(varName, "^config", ""), "([a-z])([A-Z])", (m) => m.Groups[1].Value + "_" + m.Groups[2].Value).ToLower();
ExpressionSyntax configNode = CreateElementAccess("_configuration", CreateStringLiteral(configName));
if(varType == "int")
{
configNode = CreateMemberAccessInvocation("Int32", "Parse", configNode);
}
return node.ReplaceNode(initializerNode, configNode);
}
switch(varType)
{
case "Microsoft.Extensions.Configuration.IConfigurationRoot":
case "IConfigurationRoot":
EnsureUsing("Microsoft.Extensions.Configuration");
ExpressionSyntax pathCombineArg = CreateMemberAccessInvocation("System.IO.Path", "Combine", CreateMemberAccess("AppContext", "BaseDirectory"));
ExpressionSyntax builderNode = CreateNewType("ConfigurationBuilder").WithTrailingTrivia(SyntaxFactory.EndOfLine("\r\n"));
builderNode = CreateMemberAccessInvocation(builderNode, "SetBasePath", pathCombineArg).WithTrailingTrivia(SyntaxFactory.EndOfLine("\r\n"));
ExpressionSyntax addJsonFileArg = CreateMemberAccessInvocation("System.IO.Path", "Combine", CreateMemberAccess("AppContext", "BaseDirectory"));
builderNode = CreateMemberAccessInvocationNamedArgs(builderNode, "AddJsonFile",
(null, CreateStringLiteral("appsettings.json")),
("optional", SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression))).WithTrailingTrivia(SyntaxFactory.EndOfLine("\r\n"));
if(GetGlobalSetting("env")?.ToLower() == "test")
{
builderNode = CreateMemberAccessInvocationNamedArgs(builderNode, "AddJsonFile",
(null, CreateStringLiteral("appsettings.test.json")),
("optional", SyntaxFactory.LiteralExpression(SyntaxKind.FalseLiteralExpression)));
}
builderNode = CreateMemberAccessInvocation(builderNode, "Build");
return node.ReplaceNode(initializerNode, builderNode);
case "IDataService":
{
string className = (GetGlobalSetting("env")?.ToLower() == "test" ? "MockDataService" : "DataService");
return node.ReplaceNode(initializerNode, CreateNewType(className));
}
}
errorMsgs.AppendLine($"Cannot find injection rule for {varType} {varName}");
return node;
}
#>
ãœãŒã¹ã³ãŒãïŒããã§ã¯åçå€æ°æ©èœã䜿çšãããŠãããä»»æã®ã¿ã€ãã«å²ãåœãŠãããšãã§ããŸãïŒãã€ãŸã è¡šçŸåãé«ããããã«ãæ°ããããŒã¯ãŒããèãåºããŸããã
private static IConfigurationRoot _configuration = inject;
private IDataService _service { get; } = inject;
// ...
public Customer[] SecondDemo(Person[] people)
{
int configDelayMS = inject; // we are going to inject dependency to local variables
string configServerName = inject;
}
// ...
protected static dynamic inject;
å€æäžã«ãæ¯èŒGetGlobalSettingïŒ "env"ïŒ== "test"ã䜿çšããããã®æ¡ä»¶ã«å¿ããŠãæ°ããDataServiceïŒïŒãŸãã¯æ°ããMockDataServiceïŒïŒã®ãããããæ¿å ¥ãããŸãã
å€æçµæ
private static IConfigurationRoot _configuration = new ConfigurationBuilder()
.SetBasePath(System.IO.Path.Combine(AppContext.BaseDirectory))
.AddJsonFile("appsettings.json", optional: true)
.Build();
private IDataService _service { get; } = new DataService();
// ...
public Customer[] SecondDemo(Person[] people)
{
int configDelayMS = Int32.Parse(_configuration["delay_ms"]);
string configServerName = _configuration["server_name"];
}
// ...
ãŸãã¯ããã®ããŒã«ãã貧ä¹äººãã®éçåæãšããŠäœ¿çšããããšãã§ããŸãïŒãã ããRoslynã®ãã€ãã£ãæ©èœã䜿çšããŠã¢ãã©ã€ã¶ãŒãå®è£ ããæ¹ãã¯ããã«æ£ç¢ºã§ãïŒãã«ãŒã«ã®ã³ãŒããåæãããœãŒã¹ã³ãŒãã«æ¿å ¥ããŸãã
#error our error message here
ããã«ãããã³ã³ãã€ã«æãšã©ãŒãçºçããŸãã
#warning our warning message here
ããã¯ãIDEãŸãã¯ã³ã³ãã€ã«æã«èŠåãšããŠæ©èœããŸãã
StaticAnalyzer.t4ãã³ãã¬ãŒã
<#@ include file="AopCsharp.ttinclude" #>
<#
var syntaxNode = AnalyzeLocalVariables(SyntaxNode);
syntaxNode = AnalyzeStringFormat(syntaxNode);
if(syntaxNode == SyntaxNode)
return null;
#>
<#= syntaxNode.ToFullString() #>
<#+
private SyntaxNode AnalyzeLocalVariables(SyntaxNode syntaxNode)
{
return RewriteNodes<LocalDeclarationStatementSyntax>(syntaxNode, OnAnalyzeLocalVariablesNodeVisit);
SyntaxNode OnAnalyzeLocalVariablesNodeVisit(LocalDeclarationStatementSyntax node)
{
var errorMsgs = new System.Text.StringBuilder();
string d = "";
foreach(VariableDeclaratorSyntax variableNode in node.DescendantNodes().OfType<VariableDeclaratorSyntax>().Where(w => Regex.IsMatch(w.Identifier.ToString(), "^[A-Z]")))
{
LogDebug($"variable: {variableNode.Identifier.ToString()}");
errorMsgs.Append(d + $"variable \"{variableNode.Identifier.ToString()}\" doesn't match code standard rules");
d = ", ";
}
if(errorMsgs.Length > 0)
return AddErrorMessageTrivia(node, errorMsgs.ToString());
return node;
}
}
private SyntaxNode AnalyzeStringFormat(SyntaxNode syntaxNode)
{
return RewriteLeafStatementNodes(syntaxNode, OnAnalyzeStringFormat);
SyntaxNode OnAnalyzeStringFormat(StatementSyntax node)
{
bool hasStringFormat = false;
foreach(MemberAccessExpressionSyntax memberAccessNode in node.DescendantNodes().OfType<MemberAccessExpressionSyntax>())
{
if(memberAccessNode.Name.ToString().Trim() != "Format")
continue;
string expr = memberAccessNode.Expression.ToString().Trim().ToLower();
if(expr != "string" && expr != "system.string")
continue;
hasStringFormat = true;
break;
}
if(hasStringFormat)
return AddWarningMessageTrivia(node, "Please replace String.Format with string interpolation format.");
return node;
}
}
#>
å€æçµæ
#error variable "Customers" doesn't match code standard rules
IEnumerable<Customer> Customers;
// ...
#warning Please replace String.Format with string interpolation format.
Console.Out.WriteLine(i18(String.Format("SecondDemo: {0}", "3")));
ãŸãã¯ãã¢ããªã±ãŒã·ã§ã³ãããŒã«ã©ã€ãºããããã®èªåããŒã«ãšããŠãã€ãŸã ã¯ã©ã¹å ã®ãã¹ãŠã®æååãèŠã€ããŠãé©åãªãªãœãŒã¹ã䜿çšããŠãããã眮ãæããŸãã
ResourceReplacer.t4ãã³ãã¬ãŒã
<#@ include file="AopCsharp.ttinclude" #>
<#
Dictionary<string, string> options = ExtraTagAsDictionary();
_resources = LoadResources(options["ResourceFile"]);
_resourceClass = options["ResourceClass"];
var syntaxNode = RewriteLeafStatementNodes(SyntaxNode, OnStatementNodeVisit);
#>
<#= syntaxNode.ToFullString() #>
<#+
private SyntaxNode OnStatementNodeVisit(StatementSyntax node)
{
if(!node.DescendantNodes().OfType<InvocationExpressionSyntax>().Any(w => (w.Expression is IdentifierNameSyntax) && ((IdentifierNameSyntax)w.Expression).Identifier.ToString() == "i18" ))
return node;
var errorMsgs = new System.Text.StringBuilder();
SyntaxNode syntaxNode = RewriteNodes<InvocationExpressionSyntax>(node, (n) => OnInvocationExpressionVisit(n, errorMsgs));
if(errorMsgs.Length > 0)
return AddErrorMessageTrivia(syntaxNode, errorMsgs.ToString());
return syntaxNode;
}
private SyntaxNode OnInvocationExpressionVisit(InvocationExpressionSyntax node, System.Text.StringBuilder errorMsgs)
{
if(!(node.Expression is IdentifierNameSyntax && ((IdentifierNameSyntax)node.Expression).Identifier.ToString() == "i18" ))
return node;
ArgumentSyntax arg = node.ArgumentList.Arguments.Single(); // We know that i18 method accepts only one argument. Keep in mind that it is just a demo and in real life you could be more inventive
var expr = arg.Expression;
if(!(expr is LiteralExpressionSyntax || expr is InterpolatedStringExpressionSyntax))
{
errorMsgs.AppendLine($"Argument for i18 method must be either string literal or interpolated string, but instead got {arg.Expression.GetType().ToString()}");
return node;
}
string s = expr.ToString();
if(s.StartsWith("$"))
{
(string format, List<ExpressionSyntax> expressions) = ConvertInterpolatedStringToFormat((InterpolatedStringExpressionSyntax)expr);
ExpressionSyntax stringNode = ReplaceStringWithResource("\"" + format + "\"", errorMsgs);
if(stringNode != null)
{
var memberAccess = CreateMemberAccess("String", "Format");
var arguments = new List<ArgumentSyntax>();
arguments.Add(SyntaxFactory.Argument(stringNode));
expressions.ForEach(item => arguments.Add(SyntaxFactory.Argument(item)));
var argumentList = SyntaxFactory.SeparatedList(arguments);
return SyntaxFactory.InvocationExpression(memberAccess, SyntaxFactory.ArgumentList(argumentList));
}
}
else
{
SyntaxNode stringNode = ReplaceStringWithResource(s, errorMsgs);
if(stringNode != null)
return stringNode;
}
return node;
}
private ExpressionSyntax ReplaceStringWithResource(string s, System.Text.StringBuilder errorMsgs)
{
Match m = System.Text.RegularExpressions.Regex.Match(s, "^\"(\\s*)(.*?)(\\s*)\"$");
if(!m.Success)
{
errorMsgs.AppendLine($"String doesn't match search criteria");
return null;
}
if(!_resources.TryGetValue(m.Groups[2].Value, out string resourceName))
{
errorMsgs.AppendLine($"Cannot find resource for a string {s}, please add it to resources");
return null;
}
string csharpName = Regex.Replace(resourceName, "[^A-Za-z0-9]", "_");
ExpressionSyntax stringNode = CreateMemberAccess(_resourceClass, csharpName);
if(!String.IsNullOrEmpty(m.Groups[1].Value) || !String.IsNullOrEmpty(m.Groups[3].Value))
{
if(!String.IsNullOrEmpty(m.Groups[1].Value))
{
stringNode = SyntaxFactory.BinaryExpression(SyntaxKind.AddExpression,
CreateStringLiteral(m.Groups[1].Value),
stringNode);
}
if(!String.IsNullOrEmpty(m.Groups[3].Value))
{
stringNode = SyntaxFactory.BinaryExpression(SyntaxKind.AddExpression,
stringNode,
CreateStringLiteral(m.Groups[3].Value));
}
stringNode = SyntaxFactory.ParenthesizedExpression(stringNode);
}
return stringNode;
}
private string _resourceClass;
private Dictionary<string,string> _resources;
#>
ãœãŒã¹
Console.Out.WriteLine(i18("SecondDemo: i18"));
// ...
Console.Out.WriteLine(i18($"First Name {customer.FirstName} Last Name {customer.LastName}"));
Console.Out.WriteLine("SecondDemo: 2 " + i18("First Name ") + customer.FirstName + i18(" Last Name ") + customer.LastName);
// ...
Console.Out.WriteLine(i18(String.Format("SecondDemo: {0}", "3")));
// ...
protected static string i18(string s) => s;
ããšãã°ããªãœãŒã¹ãã¡ã€ã«Demo.resxã«ã¯ã次ã®è¡ãäœæããŸããã
<data name="First Last Names Formatted" xml:space="preserve">
<value>First Name {0} Last Name {1}</value>
</data>
<data name="First Name" xml:space="preserve">
<value>First Name</value>
</data>
<data name="Last Name" xml:space="preserve">
<value>Last Name</value>
</data>
ããã³Demo.Designer.csãã¡ã€ã«ã®èªåçæãããã³ãŒã
public class Demo
{
// ...
public static string First_Last_Names_Formatted
{
get
{
return ResourceManager.GetString("First Last Names Formatted", resourceCulture);
}
}
public static string First_Name
{
get
{
return ResourceManager.GetString("First Name", resourceCulture);
}
}
public static string Last_Name
{
get
{
return ResourceManager.GetString("Last Name", resourceCulture);
}
}
}
å€æçµæïŒè£éãããæååãString.Formatã«çœ®ãæãããããå{0}æåŸã®åå{1}ããªãœãŒã¹ã䜿çšãããããšã«æ³šæããŠãã ããïŒããªãœãŒã¹ãã¡ã€ã«ã«ååšããªãè¡ããŸãã¯ãã©ãŒããããšäžèŽããªãè¡ã®å Žåããšã©ãŒã¡ãã»ãŒãžãè¿œå ãããŸã
//#error Cannot find resource for a string "SecondDemo: i18", please add it to resources
Console.Out.WriteLine(i18("SecondDemo: i18"));
// ...
Console.Out.WriteLine(String.Format(Demo.First_Last_Names_Formatted, customer.FirstName, customer.LastName));
Console.Out.WriteLine("SecondDemo: 2 " + (Demo.First_Name + " ") + customer.FirstName + (" " + Demo.Last_Name + " ") + customer.LastName);
// ...
//#error Argument for i18 method must be either string literal or interpolated string, but instead got Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax
Console.Out.WriteLine(i18(String.Format("SecondDemo: {0}", "3")));
ããã«ãå€æããŒã«ã䜿çšãããšãCïŒãã¡ã€ã«ã ãã§ãªããä»»æã®ãã¡ã€ã«ã¿ã€ãïŒãã¡ãããç¹å®ã®å¶éä»ãïŒãæäœã§ããŸããã䜿çšã®èšèªã®ASTãæ§ç¯ã§ããããŒãµãŒãããå Žåã¯ãRoslynããã®ããŒãµãŒã«çœ®ãæããã³ãŒããã³ãã©ãŒã®å®è£ ã埮調æŽãããšãæ©èœããŸããæ®å¿µãªãããRoslynã«è¿ãæ©èœãåããã©ã€ãã©ãªã®æ°ã¯éåžžã«éãããŠããããããã䜿çšããã«ã¯ããã«å€ãã®åŽåãå¿ èŠã§ãã CïŒã«å ããŠãJavaScriptããã³TypeScriptãããžã§ã¯ãã«ã¯å€æã䜿çšããŸãããCïŒã»ã©å æ¬çã§ã¯ãããŸããã
ç¹°ãè¿ãã«ãªããŸãããäŸãšãã³ãã¬ãŒãã®ã³ãŒãã¯ããã®ãããªã¢ãããŒãã®å¯èœæ§ã説æããããã«æäŸãããŠããã圌ããèšãããã«ã空ãéçã§ãã
埡æéããããšãããããŸãã
ãã®èšäºã®äž»èŠéšåã¯æ°å¹Žåã«æžãããŸããããæ®å¿µãªãããç¹å®ã®çç±ã«ãããçŸåšããå ¬éã§ããŸããã§ããã
å ã®ããŒã«ã¯.NetFrameworkã§éçºãããŸãããã.NetCoreã®MITã©ã€ã»ã³ã¹ã®äžã§ç°¡ç¥åããããªãŒãã³ãœãŒã¹ããŒãžã§ã³ã®äœæ¥ãéå§ããŸãããçŸæç¹ã§ã¯ãçµæã¯å®å šã«æ©èœãã90ïŒ ã®æºåãã§ããŠããããã€ããŒãªæ¹åãã³ãŒãã®ãã¢ã¹ã¿ã€ã«ãããã¥ã¡ã³ããšäŸã®äœæããããŸãããããããã¹ãŠããªããšãããžã§ã¯ãã«åå ããã®ãé£ãããã¢ã€ãã¢èªäœãæãªãããDXã¯ãã¬ãã£ãã«ãªããŸãã
å¶äœã«æºãã£ã人ã¯ãä»ã®äŒç€Ÿã«ç§»ãåã«å®æãããããšãã§ããªãã£ãã®ã§ããªãœãŒã¹ãå²ãåœãŠãŠäœæ¥ãç¶ããåã«ãã³ãã¥ããã£ã®åå¿ãèŠãŠã¿ãããšæããŸãããã®ãããã¯ãéçºãžã®ä»£æ¿ããŒã«ãŸãã¯ã¢ãããŒãã«ãã£ãŠåããããŠããããšã
ããŒã«ã®ã¢ã€ãã¢èªäœã¯éåžžã«ã·ã³ãã«ã§ãéçºè ã¯å®è¡å¯èœãªããŒãžã§ã³ã®å®è£ ã«åèšçŽ1ãæãè²»ãããã®ã§ãRoslynã®åªããè³æ Œãšçµéšãæã€ããã°ã©ããŒã¯ãæ°æ¥ã§ç¬èªã®ç¹å®ã®ããŒãžã§ã³ãäœæã§ãããšæããŸããçŸæç¹ã§ã¯ããããžã§ã¯ãã®ãœãŒã¹ã³ãŒãã®ãµã€ãºã¯ãäŸãšãã³ãã¬ãŒããå«ããŠçŽ150KBã§ãã
ç§ã¯å»ºèšçãªæ¹å€ãåããŠããããã§ãïŒé建èšçãªæ¹å€ãç§ãåæºãããªãã®ã§ãèºèºããªãã§ãã ããïŒã
Phil Ranginã«æè¬ããŸãïŒfillpackartïŒèšäºãæžãåæ©ä»ãã®ããããã£ã³ãã«ãWeAreDoomedãã«ãŒã«ïŒ