
ããããCïŒã¯ãåçã«éçºããç掻ãç°¡çŽ åããæ©èœãè¿œå ããŠãããããå€ãã®äººãšæ¯ã¹ãŠããã®ãããªvyæã®çç±ãæ¯æè ã«äžããŸããã ããã§ããå®ç§ã«ã¯éçããããŸããããããŠãç§ãã¡äžäººäžäººã«-圌èªèº«ã®ãã®ã§ãã
ç§ã¯ãã®ä»äºã§ç§ã«ãšã£ãŠåªå äºé ãããºãªã³ã®æ¯ãè©ŠããŠã¿ãããšãã欲æ±ã§ãããåŸã§èª¬æããã¢ã€ãã¢èªäœããã®ã©ã€ãã©ãªããã¹ãããããã®æ©äŒãšãã¹ãäŸã§ãã£ãããšã«ããã«æ³šæããŸãã ããããç 究ãšå®è£ ã®éçšã§ãäžéšã®ã¿ã³ããªã³ã§ã¯å®éã«èšèªæ§æãæ¡åŒµããããã«å®éã«çµæã䜿çšã§ããããšãããããŸããã ãããè¡ãæ¹æ³ã«ã€ããŠã¯ãæåŸã«ç°¡åã«èª¬æããŸãã ãããŸã§ã®éãå§ããŸãããã
ã»ãŒãã³ãŒã«ãšå€åã¢ãã
å®å šãªåŒã³åºãã®ã¢ã€ãã¢ã¯ãnulläžã®ã¯ã©ã¹ã®è¿·æãªãã§ãã¯ãåãé€ãããšã§ããããã¯ãå¿ èŠã§ãããšåæã«ãã³ãŒããèããè©°ãŸãããèªã¿ããããæãªããŸãã åæã«ãNullReferenceExceptionã®çµ¶ãéãªãè åšã«ãããããããšã¯æã¿ãŸããã
ãã®åé¡ã¯ã Maybeã¢ããã䜿çšããŠé¢æ°åããã°ã©ãã³ã°èšèªã§è§£æ±ºãââããŸããããã®æ¬è³ªã¯ããã¯ã·ã³ã°åŸããã€ãã©ã€ã³ã³ã³ãã¥ãŒãã£ã³ã°ã§äœ¿çšãããåã«å€ãŸãã¯Nothingå€ãå«ãŸããããšãããããšã§ãã ãã€ãã©ã€ã³ã®åã®èšç®ã§äœããã®çµæãåŸãããå Žåã次ã®èšç®ãå®è¡ãããNothingãè¿ãããå Žåã次ã®èšç®ã®ä»£ããã«Nothingãè¿ãããŸãã
CïŒã§ã¯ããã®ã¢ããã®å®è£ ã®ãã¹ãŠã®æ¡ä»¶ãäœæãããŸã-Nothingã®ä»£ããã«nullã䜿çšããããããã®Nullable <T>ããŒãžã§ã³ã¯æ§é åã«äœ¿çšã§ããŸãã ååãšããŠããã®ã¢ã€ãã¢ã¯ãã§ã«ç©ºäžã«ãããLINQã䜿çšããŠãã®ã¢ãããCïŒã§å®è£ ããèšäºãããã€ããããŸããã ãããã®1ã€ã¯Dmitry Nesteruk mezastelã«å±ããŠããŸã ã å¥ã®ãã®ããããŸãã
ãããããã®ãããªã¢ãããŒãã®ãã¹ãŠã®èªæã§ãçŽæ¥åŒã³åºãã®ä»£ããã«ã©ã ãé¢æ°ãšLINQããã®ã©ãããŒã䜿çšããå¿ èŠããããããã¢ããã䜿çšããçµæã®ã³ãŒãã¯éåžžã«ãŒãããŠããããšã«æ³šæããå¿ èŠããããŸãã ãã ããèšèªã®æ§ææ段ããªããšããããšã¬ã¬ã³ãã«å®è£ ããããšã¯ã»ãšãã©äžå¯èœã§ãã
ç§ã®èããŠããããã«ããã®ã¢ã€ãã¢ãå®è£ ããããªããšã¬ã¬ã³ããªæ¹æ³ã¯ãç§ã®æããJetBrainsïŒ Null-safety ïŒäŒç€Ÿã®äººã«ãã£ãŠãŸã äœæãããŠããªãJDKã®Kotlinèšèªã®ä»æ§ã§èŠã€ããŸããã å€æããããã«ãããã¯ãã§ã«Groovyã§ãããããã©ããå¥ã®å Žæã«ãããŸãã
ããã§ããã®å®å šãªåŒã³åºãã¹ããŒãã¡ã³ãã¯äœã§ããïŒ åŒããããšããŸãïŒ
string text = SomeObject.ToString();
SomeObjectãnullã®å Žåãæ¢ã«èª¬æããããã«ãNullReferenceExceptionãååŸããããšã¯é¿ããããŸããã ãããé¿ããããã«ãçŽæ¥åŒã³åºãæŒç®å 'ã'ã«å ããŠå®çŸ©ããŸãã å®å šãªåŒã³åºãæŒç®åãïŒã 次ã®ãšããã§ãã
string text = SomeObject?.ToString();
ãããŠå®éã«ã¯åŒã§ãïŒ
string text = SomeObject != null ? SomeObject.ToString() : null;
å®å šã«åŒã³åºãããã¡ãœãããŸãã¯ããããã£ãæ§é åãè¿ãå Žåãå²ãåœãŠãããå€æ°ã¯Nullableåã§ããå¿ èŠããããŸãã
int? count = SomeList?.Count;
éåžžã®åŒã³åºããšåæ§ã«ããããã®å®å šãªåŒã³åºãã¯ãã§ãŒã³ã§äœ¿çšã§ããŸãã次ã«äŸã瀺ããŸãã
int? length = SomeObject?.ToString()?.Length;
åŒã«å€æããŸãïŒ
int? length = SomeObject != null ? SomeObject.ToString() != null ? SomeObject.ToString().Length : null : null;
ããã«ãããè¿œå ã®é¢æ°åŒã³åºããçæããããããç§ãææ¡ããŠããå€æã®æ¬ ç¹ã®ããã€ããé ãããŠããŸãã å®éãããšãã°æ¬¡ã®åœ¢åŒã«å€æããããšãæãŸããã§ãããã
var temp = SomeObject; string text = null; if (temp != null) text = temp.ToString();
ãã ããRoslynã®ããåé·ãªæ§è³ªã«ãããäŸãè¥å€§åããéå±ã«ãªããªãããã«ãå€æãããåçŽã«ããããšã«ããŸããã ãã ããããã¯æ¬¡ã®éšåã«ãããŸãã
ãããžã§ã¯ãããºãªã³
ãåç¥ãããããŸãããã Roslynãããžã§ã¯ãã® CTPããŒãžã§ã³ãæè¿ãªãªãŒã¹ãããŸãããCïŒããã³VBéçºè ã¯ããããŒãžã³ãŒãã䜿çšããŠèšèªã³ã³ãã€ã©ãå®å šã«æžãæãããããã®ã³ã³ãã€ã©ãžã®ã¢ã¯ã»ã¹ã¯APIãšããŠå ¬éãããŸããã ããã«ãããéçºè ã¯å€ãã®äŸ¿å©ãªããšãã§ããŸããããšãã°ãéåžžã«äŸ¿å©ã§ç°¡åãªåæãæé©åãã³ãŒãã®çæãã¹ã¿ãžãªãããã³å Žåã«ãã£ãŠã¯ç¬èªã®DSLã®æ¡åŒµæ©èœãšã³ãŒãä¿®æ£ã®èšè¿°ãå¯èœã§ãã 確ãã«ãããã«ãªãªãŒã¹ãããããšã¯ãªããVisual Studioã®1ã€ã®ããŒãžã§ã³ãä»ããŠæ¢ã«ãªãªãŒã¹ãããŸãããä»ãããæãããã§ãã
åé¡ã®è§£æ±ºã«ç§»ããŸãããããŸãããã®èšèªã®æ¡åŒµæ©èœãå®éã«ã©ã®ããã«äœ¿çšãããŠããããæ³åããŠã¿ãŠãã ããã æããã«ïŒãã€ãã®ããã«ããæ°ã«å ¥ãã®IDEã§ã³ãŒããèšè¿°ããå¿ èŠã«å¿ããŠå®å šãªåŒã³åºãæŒç®åã䜿çšããã³ã³ãã€ã«äžã«ProjectãæŒããŸããProjectRoslynã䜿çšããŠäœæããããã°ã©ã ã¯ãããããã¹ãŠãæ§æçã«æ£ããCïŒã³ãŒãã«å€æãããã¹ãŠãã³ã³ãã€ã«ãããŸããã 倱æãããŠããã ããŸãããRoslynã§ã¯ãçŸåšã®csc.exeã³ã³ãã€ã©ã®åäœã劚害ããããšã¯ã§ããŸããã åãvNextã¹ã¿ãžãªã§ãã³ã³ãã€ã©ããã®ãããŒãžã«ãŠã³ã¿ãŒããŒãã«çœ®ãæãããããšããã®ãããªæ©äŒãçŸããå¯èœæ§ããããŸãã ãããã圌女ãããªããªã£ãŠããéã
åæã«ããã§ã«2ã€ã®åé¿çããããŸãã
- åãRoslyn APIã䜿çšããŠãçŸåšã®csc.exeã®ä»£ããã«ç¬èªã®ã³ã³ãã€ã©ãäœæããcsc.exeãç¬èªã®ã¢ããã°ã«çœ®ãæããŠãã«ãã·ã¹ãã ãå€æŽããããšãã§ããŸããã³ãŒãã
- ã³ã³ãœãŒã«ããã°ã©ã ããœãŒã¹ã³ãŒããã¡ã€ã«ãå€æããåãåã£ãæ°ãããœãŒã¹ãObjãã©ã«ããŒã«ä¿åãããã«ãåã¿ã¹ã¯ãšããŠäœ¿çšã§ããŸãã ãã«ãåãã§ãŒãºã®xamlãã¡ã€ã«ã.g.csãã¡ã€ã«ã«å€æããããšããWPFã¯çŸåšéåžžã«ãã䌌ãæ¹æ³ã§ã³ã³ãã€ã«ãããŠããŸãã
Project Roslynã¯ããã€ãã®ã¿ã€ãã®æ©èœãæäŸããŸãããéèŠãªæ©èœã®1ã€ã¯ãæœè±¡æ§æããªãŒã®æ§ç¯ãåæãããã³å€æã§ãã ããã«äœ¿çšããã®ã¯ããã®æ©èœã§ãã
å®è£
ãã¡ããã以äžã«æžãããŠãããã®ã¯ãã¹ãŠåãªãäŸã§ãããå€ãã®æ¬ é¥ã«æ©ãŸãããŠãããå€§å¹ ãªæ¹åããªããã°å®éã«äœ¿çšããããšã¯ã§ããŸãããããã®ãããªããšãååçã«è¡ãããããšã瀺ããŠããŸãã
å®è£ ã«ç§»ããŸãããã ããã°ã©ã ãäœæããã«ã¯ãæåã«Roslyn SDKãã€ã³ã¹ããŒã«ããå¿ èŠããããŸããããã¯ã ãªã³ã¯ããããŠã³ããŒãã§ããVisual Studio 2010çšã®Service Pack 1ãšVisual Studio 2010 SDK SP1ãæåã«ã€ã³ã¹ããŒã«ããå¿ èŠããããŸãã
ããããã¹ãŠã®æäœã®åŸãRoslynãµãé ç®ãæ°ãããããžã§ã¯ããäœæããããã®ã¡ãã¥ãŒã«è¡šç€ºãããŸããããã«ã¯ãããã€ãã®ãããžã§ã¯ããã³ãã¬ãŒããå«ãŸããŠããŸãïŒãã®äžéšã¯IDEã«çµ±åã§ããŸãïŒã ç°¡åãªã³ã³ãœãŒã«ã¢ããªã±ãŒã·ã§ã³ãäœæããŸãã
ããšãã°ã次ã®ããœãŒã¹ã³ãŒããã䜿çšããŸãã
public class Example { public const string CODE = @"using System; using System.Linq; using System.Windows; namespace HelloWorld { public class TestClass { public string TestField; public string TestProperty { get; set; } public string TestMethod() { return null; } public string TestMethod2(int k, string p) { return null; } public TestClass ChainTest; } public class OtherClass { public void Test() { TestClass test; string testStr1; testStr1 = test?.TestField; string testStr3 = test?.TestProperty; string testStr4 = test?.TestMethod(); string testStr5 = test?.TestMethod2(100, testStr3); var test3 = test?.ChainTest?.TestField; } } }"; }
ãã®ãœãŒã¹ã³ãŒãã¯ãå®å šãªåŒã³åºãã¹ããŒãã¡ã³ããé€ããæ§æçã«æ£ããã ãã§ãªããã³ã³ãã€ã«ãããŸãããããã¯å€æã«ã¯å¿ èŠãããŸããã
ãŸãããœãŒã¹ãã¡ã€ã«ããæœè±¡æ§æããªãŒãæ§ç¯ããå¿ èŠããããŸãã ããã¯2ã€ã®æ¹æ³ã§è¡ãããŸãã
SyntaxTree tree = SyntaxTree.ParseCompilationUnit(Example.CODE); SyntaxNode root = tree.Root;
æ§æããªãŒã¯SyntaxTreeã¯ã©ã¹ã«ãã£ãŠå®çŸ©ãããå¥åŠãªããšã«ãããŒã¹SyntaxNodeã¿ã€ãããç¶æ¿ããããŒãã®ããªãŒã§ãããåããŒãã¯ç¹å®ã®åŒïŒãã€ããªåŒãæ¡ä»¶åŒãã¡ãœããåŒã³åºãåŒãããããã£å®çŸ©ãå€æ°ïŒãè¡šããŸãã åœç¶ãSyntaxNodeã®äžäœã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ã«ãã£ãŠã絶察ã«CïŒæ§é ã衚瀺ã§ããŸãã ããã«ãSyntaxTreeã¯ã©ã¹ã«ã¯ãããŒã¯ãŒãããªãã©ã«ãèå¥åãå¥èªç¹ïŒäžæ¬åŒ§ãæ¬åŒ§ãã³ã³ããã»ãã³ãã³ïŒã®æå°æ§æãããã¯ã®ã¬ãã«ã§è§£æãããœãŒã¹ã³ãŒããå®çŸ©ããSyntaxTokenã»ãããå«ãŸããŠããŸãã æåŸã«ãSyntaxTree inã«ã¯SyntaxTriviaèŠçŽ ïŒã³ãŒããç解ããäžã§éèŠã§ã¯ãªãèŠçŽ ïŒãå«ãŸããŸããã¹ããŒã¹ãšã¿ããã³ã¡ã³ããããªããã»ããµãã£ã¬ã¯ãã£ããªã©ã§ãã
ããã§ã1ã€ã®å°ããªè©³çŽ°ãç¥ã£ãŠããå¿ èŠããããŸã-Roslynã¯ãã¡ã€ã«ã®è§£æã«éåžžã«å¯å®¹ã§ãã ã€ãŸããè¯ãæ¹æ³ã§ã¯ãããŸãããåæã®ããã«æ§æçã«æ£ãããœãŒã¹ã³ãŒããæäŸããå¿ èŠããããŸããå®éãäœããã®æ¹æ³ã§äœããã®ããã¹ãã絶察ã«äœããã®ASTã«å€æããããšããŸãã æ§æçã«æ£ãããªãã³ãŒããå«ããŸãã ãã®äºå®ã䜿çšããŸãã æ§ææšãæ§ç¯ããŠãRoslynãå®å šãªåŒã³åºãæŒç®åãæšã«è¡šç€ºããæ¹æ³ã調ã¹ãŠã¿ãŸãããã
Roslynã®èŠ³ç¹ããèŠããšããã¹ãã¯ãã¹ãŠãã¹ãã§ãããïŒ.TestFieldã¯ãæ¡ä»¶-ãtestããåŒãwhen trueã-ã.TestFieldããããã³ç©ºã®åŒãwhen wrongããæã€äžé æŒç®åã§ãã ãã®æ å ±ãæŠåšã«ãããªãŒãå€æããŸãã ããã§ãRoslynã®å¥ã®æ©èœã«ééããŸã-æ§ç¯ããæ§æããªãŒã¯äžå€ã§ããã€ãŸããæ¢åã®æ§é ãçŽæ¥ä¿®æ£ããããšã¯ã§ããŸããã ããããããã¯åé¡ã§ã¯ãããŸããã Roslynã¯ããã®ãããªæäœã«SyntaxRewriterã¯ã©ã¹ã䜿çšããããšããå§ãããŸããããã¯ãSyntaxVisitorã¯ã©ã¹ãç¶æ¿ããŸããSyntaxVisitorã¯ã©ã¹ã¯ãåã®ãšãããæªåé«ãVisitorãã¿ãŒã³ãå®è£ ããŸãã ç¹å®ã®åã¿ã€ãã®ããŒããžã®èšªåãåŠçããå€ãã®ä»®æ³ã¡ãœãããå«ãŸããŠããŸãïŒVisitFieldDeclarationãVisitEnumMemberDeclarationãªã©ãåèšã§çŽ180åãããŸãïŒã
åå«ã®SyntaxRewriterã¯ã©ã¹ãäœæããVisitorConditionalExpressionã¡ãœããããªãŒããŒã©ã€ãããå¿ èŠããããŸãããã®ã¡ãœããã¯ã蚪åè ãäžé æŒç®åã§ããåŒããã€ãã¹ãããšãã«åŒã³åºãããŸãã 次ã«ãç¹ã«å°ãããããå®è£ ã³ãŒãå šäœãæäŸããããã€ãã®èª¬æã®ã¿ãè¿œå ããŸãã
// public class SafeCallRewriter : SyntaxRewriter { // ?. public bool IsSafeCallRewrited { get; set; } protected override SyntaxNode VisitConditionalExpression(ConditionalExpressionSyntax node) { if (IsSafeCallExpression(node)) { // expression , null string identTxt = node.Condition.GetText(); ExpressionSyntax ident = Syntax.ParseExpression(identTxt); // expression , != null string exprTxt = node.WhenTrue.GetText(); exprTxt = exprTxt.Substring(1, exprTxt.Length - 1);// exprTxt = identTxt + '.' + exprTxt; ExpressionSyntax expr = Syntax.ParseExpression(exprTxt); ExpressionSyntax synt = Syntax.ConditionalExpression(// condition: Syntax.BinaryExpression(// ident != null SyntaxKind.NotEqualsExpression, left: ident, // - right: Syntax.LiteralExpression(SyntaxKind.NullLiteralExpression)), // null whenTrue: expr, whenFalse: Syntax.LiteralExpression(SyntaxKind.NullLiteralExpression)); IsSafeCallRewrited = true; return synt; } return base.VisitConditionalExpression(node); } // private bool IsSafeCallExpression(ConditionalExpressionSyntax node) { return node.WhenTrue.GetText()[0] == '.'; } }
ç§ã®æåã®å®è£ ã¯ASTã®è«çæ§é ã§ã®ã¿åäœããããšããåŒã®ããã¹ãè¡šçŸã§ã¯åäœã軜èŠããããšããŸãããããã®è€éãã¯ããã«èãããããã¹ãŠã®å¶éãè¶ ãå§ããŸããã å®å šãªåŒã³åºããšãã®ã¿ã€ããå®çŸ©ããããã®é¢æ°ã¯ããã£ãŒã«ããšããããã£ãã¡ãœããã®åŒã³åºããå®å šãªåŒã³åºãã®ãã§ãŒã³ã®3ã€ã ãã§ããããããã¯ãã¹ãŠãSyntaxNodeã¯ã©ã¹ã®ç°ãªãç¶æ¿è ã§ãããšæãããããã§ãã å®å šã«åãåºããåŸãç§ã¯æåã®ãªãã·ã§ã³ããŽãç®±ã«æšãŠã2åç®ã¯RoslynãæäŸãã䟿å©ãªGetTextããã³ParseExpressioné¢æ°ãšãè¡ã¬ãã«ã§ã®ããã€ãã®æ±ãããã¯ã䜿çšããŸãã:)ã
ãŸããæ§æããŒãïŒãã®å Žåã¯ConditionalExpressionïŒã®äœæããã»ã¹ãšããã®å Žåã®ååä»ããã©ã¡ãŒã¿ãŒã®ãããªCïŒãããã®äœ¿çšã®å¿«é©æ§ã«ã泚æããããšããå§ãããŸãã ããã§ãªãå Žåãæ§æããŒããæ§ç¯ããããã»ã¹ã§ã倢äžã«ãªãå¯èœæ§ããããŸãã
ã¡ã€ã³ããã·ãŒãžã£ã®ã³ãŒãã¯æ¬¡ã®ãšããã§ãã
static void Main(string[] args) { // SyntaxTree tree = SyntaxTree.ParseCompilationUnit(Example.CODE); SyntaxNode root = tree.Root; SafeCallRewriter rewriter = new SafeCallRewriter(); do { rewriter.IsSafeCallRewrited = false; // , root = rewriter.Visit(root); } while (rewriter.IsSafeCallRewrited);// 1 maybe- root = root.Format();// Ctrl+K, Ctrl+D Console.WriteLine(root.ToString()); }
ã³ãŒã«ãã§ãŒã³ãåŠçããã«ã¯ãããªãŒã®ããã€ãã®æžãæããå¿ èŠã§ããããšã説æããŸãã ãã¡ãããããã¯ååž°ã«ãã£ãŠè¡ãããšãã§ããŸããããããããã®å Žåãã³ãŒãããŒãããã ãã§ãã ãŸããFormatã®çŽ æŽãããæ©èœã«ã泚æããŠãã ããã æå®ãããã¹ã¿ã€ã«ã®ã³ãŒãã®æžåŒèšå®ãããã°ã©ã ã§è¡ããŸãã å¿ èŠãªãã¹ãŠã®SyntaxTriviaãASTã«è¿œå ããŸãã
çµæãšããŠã次ã®ã³ãŒãããããŸãã
using System; using System.Linq; using System.Windows; namespace HelloWorld { public class TestClass { public string TestField; public string TestProperty { get; set; } public string TestMethod() { return null; } public string TestMethod2(int k, string p) { return null; } public TestClass ChainTest; } public class OtherClass { public void Test() { TestClass test; string testStr1; testStr1 = test != null ? test.TestField : null; string testStr3 = test != null ? test.TestProperty : null; string testStr4 = test != null ? test.TestMethod() : null; string testStr5 = test != null ? test.TestMethod2(100, testStr3) : null; var test3 = test != null ? test.ChainTest != null ? test.ChainTest.TestField : null : null; } } }
ãã®ãããRoslynãšã®æåã®ç¥ãåãã¯æåããèšèªæ¡åŒµæ©èœãäœæããããšã¯å¿ ãããå¿ èŠã§ã¯ãããŸããããäžè¬çã«ãã®èŠéãã¯éåžžã«è¯å¥œã§ãã ãããããæ奜家ãããå Žåãããã¯ããæ·±ããããçå£ã«è¡ãããšãã§ããŸãã CïŒã«ã¯ããŸã 足ããªããã®ããããããããŸãã :)
PS Roslynã®ãã®ãããªäœ¿çšã®ãã1ã€ã®äŸã¯ãç§ã倧ãã«å©ããŠãããŸããã