在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
一、前言 IronRuby是.NET下的一个Ruby实现,此外还有Ruby.net这一开源项目,二者的主要区别是IronRuby利用了Microsoft最新推出的DLR,而Ruby.net则是完全利用原有的CLR实现的。IronRuby入门可参阅http://msdn.microsoft.com/zh-cn/magazine/dd434651.aspx。关于IronRuby的一些基本操作,本文不会涉及,本文仅仅是IronRuby对Ruby操作的一个具体实例。其中包括对所有Ruby类的类名,方法名以及参数列表的获取与显示相关的树结构。究其原因采用IronRuby来进行操作,主要是因为通过Ruby的反射可以获取到Ruby方法名列表,但是获取不到方法的参数列表与参数名称。此文仅供参考,因为本人也对IronRuby接触不是很久,基本上是摸索出来的,难免会有错误的地方。
二、类图设计 相关类图设计如下,其中RubyScriptEngine主要负责通过IronRuby来获取和构造相关的类名、方法名与参数列表以及之间的相关关系。TreeDrawer主要负责设计类名、方法名与参数列表相对应的树形结构图。
三、详细设计 (1)RubyScriptEngine主要负责通过IronRuby来获取和构造相关的类名、方法名与参数列表以及之间的相关关系。RubyScriptEngine将Ruby文件进行加载,然后动态获取文件中包含的类、方法与方法参数列表。 具体代码如下:
1 public static class RubyScriptEngine
2 { 3 private static readonly ScriptEngine engine = null; 4 5 static RubyScriptEngine() 6 { 7 engine = Ruby.CreateEngine(); 8 } 9 10 public static bool InitRelativeFiles(string directory) 11 { 12 if (!Directory.Exists(directory)) 13 { 14 return false; 15 } 16 17 string[] files = Directory.GetFiles(directory); 18 for (int index = 0; index < files.Length; index++) 19 { 20 InitRelativeFile(files[index]); 21 } 22 23 return true; 24 } 25 26 public static bool InitRelativeFile(string fileName) 27 { 28 if (!File.Exists(fileName)) 29 { 30 return false; 31 } 32 33 try 34 { 35 FileInfo fileInfo = new FileInfo(fileName); 36 if (string.Equals(fileInfo.Extension, ".rb", StringComparison.CurrentCultureIgnoreCase)) 37 { 38 engine.ExecuteFile(fileName); 39 } 40 } 41 catch 42 { 43 return false; 44 } 45 46 return true; 47 } 48 49 public static IList<string> GetClassNames() 50 { 51 return engine.Runtime.Globals.GetVariableNames().ToList(); 52 } 53 54 public static IList<ClassItem> GetClassesInfos() 55 { 56 IList<string> names = GetClassNames(); 57 IList<ClassItem> items = new List<ClassItem>(); 58 foreach (string name in names) 59 { 60 items.Add(GetClassInfo(name)); 61 } 62 63 return items; 64 } 65 66 public static ClassItem GetClassInfo(string className, params object[] parameters) 67 { 68 RubyClass rubyClass = engine.Runtime.Globals.GetVariable(className); 69 dynamic instance = engine.Operations.CreateInstance(rubyClass, parameters); 70 ClassItem classItem = new ClassItem(className); 71 72 IList<string> memberNames = engine.Operations.GetMemberNames(instance); 73 74 MethodItem methodItem = null; 75 ParameterItem parameterItem = null; 76 foreach (string memberName in memberNames) 77 { 78 RubyMethodInfo methodInfo = rubyClass.GetMethod(memberName) as RubyMethodInfo; 79 80 if (methodInfo == null) 81 { 82 continue; 83 } 84 85 methodItem = new MethodItem(memberName,className); 86 87 RubyArray parameterArray = methodInfo.GetRubyParameterArray(); 88 SimpleAssignmentExpression[] expressions = methodInfo.Parameters.Optional; 89 90 for (int index = 0; index < parameterArray.Count; index++) 91 { 92 RubyArray vas = parameterArray[index] as RubyArray; 93 string type = vas[0].ToString(); 94 string name = vas[1].ToString(); 95 parameterItem = new ParameterItem(name); 96 if (type == "rest") 97 { 98 parameterItem.DefaultName = "*" + name; 99 parameterItem.Description = RubyResource.ArrayParamDesc; 100 } 101 else if (type == "opt") 102 { 103 for (int eindex = 0; eindex < expressions.Length; eindex++) 104 { 105 SimpleAssignmentExpression ex = expressions[eindex]; 106 Variable variable = ex.Left as Variable; 107 if (!string.Equals(variable.Name, name)) 108 { 109 continue; 110 } 111 112 Literal literal = ex.Right as Literal; 113 parameterItem.DefaultName = name; 114 parameterItem.DefaultValue = literal.Value; 115 parameterItem.Description = RubyResource.DefaultParamDesc; 116 } 117 } 118 else if (type == "block") 119 { 120 parameterItem.DefaultName = "&" + name; 121 parameterItem.Description = RubyResource.BlockParamDesc; 122 } 123 else 124 { 125 parameterItem.DefaultName = name; 126 } 127 methodItem.Parameters.Add(parameterItem); 128 } 129 classItem.Methods.Add(methodItem); 130 } 131 132 return classItem; 133 } 134 } 其中相关方法如下: public static bool InitRelativeFiles(string directory) 根据目录加载目录下的Ruby文件 public static bool InitRelativeFile(string fileName) 根据文件名加载该Ruby文件 public static IList<string> GetClassNames() 获取所有的类名 public static IList<ClassItem> GetClassesInfos() 获取所有类的信息 public static ClassItem GetClassInfo(string className, params object[] parameters) 根据类名和类的构造函数参数获取对应的类信息
类的信息显示效果如下(左侧显示类的信息,右侧编辑器显示类的基本结构):
(2)TreeDrawer主要用于绘制类的树结构,根据不同的类结构显示不同的效果。这里是用Winform来显示的,本来打算用Silverlight来实现,但是由于时间关系,将就着这样算了。当然,Silverlight显示的效果比Winform强多了,而且,本人Silverlight水平比Winform熟练很多(以前项目中用Silverlight动态绘制相关图形,因此比较熟悉).... TreeDrawer的主要方法为以下2个:
1 public Bitmap CreateImage(ClassItem classItem, Font font) 2 { 3 if (classItem == null || font == null) 4 { 5 return null; 6 } 7 8 ClassBlock classBlock = CreateCurrentClassBlock(classItem); 9 AddLinesAndBlockTexts(classBlock, font); 10 11 Bitmap bitmap = new Bitmap(3 * BLOCK_WIDTH + 2 * BLOCK_INNER_WIDTH, this.Height); 12 Graphics graphics = Graphics.FromImage(bitmap); 13 graphics.Clear(Color.White); 14 Pen ellipsePen = new Pen(Color.Blue, 2); 15 16 foreach (Line line in this.Lines) 17 { 18 graphics.DrawLine(ellipsePen, line.X1, line.Y1, line.X2, line.Y2); 19 if (line.HasArrow) 20 { 21 PointF[] points = CreateArrowPoints(new PointF(line.X1, line.Y1), 22 new PointF(line.X2, line.Y2), ARROW_LENGTH, RELATIVE_VALUE); 23 DrawArrowHead(graphics, points); 24 } 25 } 26 27 foreach (BlockText content in this.Contents) 28 { 29 graphics.DrawString(content.Content, font, Brushes.Black, content.StartX, content.StartY); 30 } 31 32 return bitmap; 33 } 34 35 private ClassBlock CreateCurrentClassBlock(ClassItem classItem) 36 { 37 int originalParamY = 0; 38 int lastParamY = 0; 39 int currentParamY = 0; 40 int currentMethodY = 0; 41 int startMethodX = BLOCK_WIDTH + BLOCK_INNER_WIDTH; 42 int startParamX = 2 * BLOCK_WIDTH + 2 * BLOCK_INNER_WIDTH; 43 44 List<MethodBlock> methodBlocks = new List<MethodBlock>(); 45 foreach (MethodItem methodItem in classItem.Methods) 46 { 47 int paramsCount = methodItem.Parameters.Count; 48 if (paramsCount > 0) 49 { 50 lastParamY += paramsCount * (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT); 51 } 52 else 53 { 54 lastParamY += (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT); 55 currentParamY += (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT); 56 } 57 58 currentMethodY = ((lastParamY - BLOCK_INNER_HEGIHT - originalParamY) / 2 + originalParamY - (BLOCK_HEIGHT / 2)); 59 originalParamY = lastParamY; 60 61 MethodBlock methodBlock = new MethodBlock(new Point(startMethodX, currentMethodY), BLOCK_WIDTH, BLOCK_HEIGHT); 62 methodBlock.Content = methodItem.Name; 63 methodBlocks.Add(methodBlock); 64 foreach (ParameterItem parameterItem in methodItem.Parameters) 65 { 66 ParameterBlock parameterBlock = new ParameterBlock(new Point(startParamX, currentParamY), BLOCK_WIDTH, BLOCK_HEIGHT); 67 parameterBlock.Content = parameterItem.DefaultName; 68 methodBlock.ParameterBlocks.Add(parameterBlock); 69 currentParamY += (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT); 70 } 71 } 72 73 if (lastParamY > 0) 74 { 75 lastParamY -= BLOCK_INNER_HEGIHT; 76 } 77 else 78 { 79 lastParamY = BLOCK_HEIGHT; 80 } 81 82 this.Height = lastParamY; 83 84 Point classStartPoint = new Point(0, 0); 85 if (classItem.Methods.Count > 1) 86 { 87 int y = (methodBlocks.Last().LeftBottomPoint.Y - methodBlocks.First().LeftBottomPoint.Y) / 2; 88 classStartPoint = new Point(0, y); 89 } 90 91 ClassBlock classBlock = new ClassBlock(classStartPoint, BLOCK_WIDTH, BLOCK_HEIGHT); 92 classBlock.Content = classItem.Name; 93 foreach (MethodBlock methodBlock in methodBlocks) 94 { 95 classBlock.MethodBlocks.Add(methodBlock); 96 } 97 98 return classBlock; 99 }
public Bitmap CreateImage(ClassItem classItem, Font font) 主要负责绘制Bitmap图片 private ClassBlock CreateCurrentClassBlock(ClassItem classItem) 主要负责将ClassItem(类信息形式)转换成ClassBlock (坐标形式),并负责计算相应的坐标
类的树结构显示效果如下:
当然,还可以定义其他格式的类,显示的效果根据类的不同绘制相应的树结构。其中,TreeDrawer中比较简单的算法会自动设置合理的坐标,以生成相应的树结构坐标。本处一切以简单进行处理,不然的话,参数设置是比较多的。
四、总结 IronRuby是.NET下的一个Ruby实现 ,对于实现单个类的操作来说,用.NET 4.0中的Dynamic更加方便与美观,如调用PersonClass类的nonArgsMethod方法即可写成如下格式: dynamic globals= engine.Runtime.Globals; dynamic apple = globals.PersonClass.@new(); //构造实例 apple.nonArgsMethod(); //调用方法
本文中采用如下代码进行调用,主要是为了通用性处理。通过类名称以及构造函数的参数来动态获取类的信息。 RubyClass rubyClass = engine.Runtime.Globals.GetVariable(className); dynamic instance = engine.Operations.CreateInstance(rubyClass, parameters); ClassItem classItem = new ClassItem(className); IList<string> memberNames = engine.Operations.GetMemberNames(instance);
源代码下载地址:IronRuby Ruby类树结构源码
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论