失敗した。原因不明。
成果物
前回まで
失敗した出力結果
using System; MyNamespace { MyClass { static void Main(string[] args) { Console.WriteLine("Hello world"); } } }
namespace
, class
のキーワードが出ていない。
期待値
... namespace MyNamespace { class MyClass { ...
原因
継承したGenerate()
メソッドが正しく呼ばれていない。子でなく親のメソッドが呼ばれている。つまりCodeNamespace
やCodeClass
でなくCodeNode
のメソッドが呼ばれてしまっている。なぜ? わからない。
推測
もしや別の型扱いなのか? interfaceを継承したクラスをさらに継承した孫クラスは、そのinterfaceを持っていない別の型として見られるの? INode
というinterface型でAdd()
やGetChildren()
したせいで、親であるCodeNode
クラスになってしまったのか?
対処案
仮にそうだとして、じゃあどうやって子クラスのメソッドを呼べばいいんだ? ふつう何層も継承したら最子孫クラスのメソッドが呼ばれるはずでは? interfaceを取っ払ったら呼出元は何の型にすればいいのさ? 親の型にしたらそれこそ親メソッドが呼ばれてしまうし。わからん!
コード
継承関係は以下。
- INode
- CodeNode
- CodeBlock
- CodeNamespace
- CodeClass
- CodeBlock
- CodeNode
CodeNamespace.cs
class CodeNamespace: CodeBlock { public CodeNamespace(string value, StyleType style=StyleType.NewLine) : base(value, style) {} public new string Generate() => $"namespace ${Value}"; }
これ。namespace
という文字列を結合した値を返している。こいつが呼ばれるはずだった。
CodeClass.cs
class CodeClass: CodeBlock { public CodeClass(string value, StyleType style=StyleType.NewLine) : base(value, style) {} public new string Generate() => $"class ${Value}"; }
これ。class
という文字列を結合した値を返している。こいつが呼ばれるはずだった。
以下は今回無関係。
CodeBlock.cs
class CodeBlock: CodeNode { public enum StyleType { None, Minimum, Space, NewLine }; public StyleType Style { get; private set; } public CodeBlock(string value, StyleType style=StyleType.NewLine) : base(value) => Style = style; }
以下のGenerate()
。こいつが呼ばれているせいでnamespace
やclass
が出なかったと思われる。
CodeNode.cs
class CodeNode : INode { protected string Value { get; } protected IList<INode> children { get; set; } protected IReadOnlyList<INode> Children { get; } public CodeNode(string value) { children = new List<INode>(); Children = new ReadOnlyCollection<INode>(children); Value = value; } public IReadOnlyList<INode> GetChildren() => Children; public INode Add(INode node) { children.Add(node); return this; } public string Generate() => Value; }
以下は今回無関係? いやINode
が問題か? わからん。
CodeTree.cs
class CodeTree { protected IList<INode> children; public IReadOnlyList<INode> Children { get; } public CodeTree() { children = new List<INode>(); Children = new ReadOnlyCollection<INode>(children); } public CodeTree Add(INode node) { children.Add(node); return this; } public IReadOnlyList<INode> GetChildren() => Children; }
以下でGenerate()
を呼び出している。
CodeGenerator.cs
class CodeGenerator { public string Indent { get; set; } = " "; private StringBuilder builder; private BlockCodeGenerator blockGen; public CodeGenerator() { this.builder = new StringBuilder(); this.blockGen = new BlockCodeGenerator(this.builder, Indent); } public string Generate(CodeTree tree) { builder.Clear(); foreach(INode node in tree.GetChildren()) { _Generate(node, 0); } return builder.ToString().TrimStart('\n'); // 先頭の改行を1つ削除 } private void _Generate(INode node, int indent) { builder.Append("\n"); builder.Insert(builder.Length, Indent, indent).Append(node.Generate()); blockGen.Blocking(node, indent); if (0 < node.GetChildren().Count) { indent++; foreach(INode n in node.GetChildren()) { _Generate(n, indent); } indent--; } blockGen.Blocked(node, indent); } class BlockCodeGenerator { ... } }
以下が呼出元。ちゃんとCodeNamespace
とCodeClass
を使っている。
Program.cs
class Program { static void Main(string[] args) { var gen = new CodeGenerator(); var tree = new CodeTree() .Add(new CodeNode("using System;")) .Add(new CodeNode("")) .Add(new CodeNamespace("MyNamespace") .Add(new CodeClass("MyClass") .Add(new CodeBlock("static void Main(string[] args)") .Add(new CodeNode(@"Console.WriteLine(""Hello world"");"))))); Console.WriteLine(gen.Generate(tree)); } }
所感
これはC#言語に対する理解が不足しているからだと思われる。チュートリアル完了させたから書けるだろうと調子こいてたらこのザマだよ。続きのC#ツアーをやるべきか。
その前にC#構文のセミコロンについてと、dotnet
/csc
コマンドについて少しまとめておきたい。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13 ※
- bash 4.4.12(1)-release ※
- SQLite 3.29.0 ※
- C# dotnet 3.0.100 ※
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux