程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#中IronRuby實現對Ruby類相關信息的樹結構顯示

C#中IronRuby實現對Ruby類相關信息的樹結構顯示

編輯:關於C#

一、前言

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文件進行加載,然後動態獲取文件中包含的類、方法與方法參數列表。

具體代碼如下:

public static class RubyScriptEngine
    {
        private static readonly ScriptEngine engine = null;
    
        static RubyScriptEngine()
        {
            engine = Ruby.CreateEngine();
        }
    
        public static bool InitRelativeFiles(string directory)
        {
            if (!Directory.Exists(directory))
            {
                return false;
            }
                
            string[] files = Directory.GetFiles(directory);
            for (int index = 0; index < files.Length; index++)
            {
                InitRelativeFile(files[index]);
            }
    
            return true;
        }
    
        public static bool InitRelativeFile(string fileName) 
        {
            if (!File.Exists(fileName))
            {
                return false;
            }
    
            try
            {
                FileInfo fileInfo = new FileInfo(fileName);
                if (string.Equals(fileInfo.Extension, ".rb", StringComparison.CurrentCultureIgnoreCase))
                {
                    engine.ExecuteFile(fileName);
                }
            }
            catch
            {
                return false;
            }
               
            return true;
        }
    
        public static IList<string> GetClassNames()
        {
           return  engine.Runtime.Globals.GetVariableNames().ToList();
        }
    
        public static IList<ClassItem> GetClassesInfos()
        {
            IList<string> names = GetClassNames();
            IList<ClassItem> items = new List<ClassItem>();
            foreach (string name in names)
            {
                items.Add(GetClassInfo(name));
            }
    
            return items;
        }
    
        public static ClassItem GetClassInfo(string className, params object[] parameters) 
        {
            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);
    
            MethodItem methodItem = null;
            ParameterItem parameterItem = null;  
            foreach (string memberName in memberNames)
            {
                RubyMethodInfo methodInfo = rubyClass.GetMethod(memberName) as RubyMethodInfo;
    
                if (methodInfo == null)
                {
                    continue;
                }
    
                methodItem = new MethodItem(memberName,className);
    
                RubyArray parameterArray = methodInfo.GetRubyParameterArray();
                SimpleAssignmentExpression[] expressions = methodInfo.Parameters.Optional;  
    
                for (int index = 0; index < parameterArray.Count; index++)
                {           
                    RubyArray vas = parameterArray[index] as RubyArray;
                    string type = vas[0].ToString();
                    string name = vas[1].ToString();
                    parameterItem = new ParameterItem(name);
                    if (type == "rest")
                    {
                        parameterItem.DefaultName = "*" + name;
                        parameterItem.Description = RubyResource.ArrayParamDesc;
                    }
                    else if (type == "opt")
                    {
                        for (int eindex = 0; eindex < expressions.Length; eindex++)
                        {
                            SimpleAssignmentExpression ex = expressions[eindex];
                            Variable variable = ex.Left as Variable;
                            if (!string.Equals(variable.Name, name))
                            {
                                continue;
                            }
    
                            Literal literal = ex.Right as Literal;           
                            parameterItem.DefaultName = name;
                            parameterItem.DefaultValue = literal.Value;
                            parameterItem.Description = RubyResource.DefaultParamDesc;
                        }
                    }
                    else if (type == "block")
                    {
                        parameterItem.DefaultName = "&" + name;
                        parameterItem.Description = RubyResource.BlockParamDesc;
                    }
                    else
                    {
                        parameterItem.DefaultName = name;
                    }
                    methodItem.Parameters.Add(parameterItem);        
                }
                classItem.Methods.Add(methodItem);
            }
    
            return classItem;
        }
    }

其中相關方法如下:

publicstaticbool InitRelativeFiles(string directory)  根據目錄加載目錄下的Ruby文件

publicstaticbool InitRelativeFile(string fileName)    根據文件名加載該Ruby文件

publicstatic IList<string> GetClassNames()            獲取所有的類名

publicstatic IList<ClassItem> GetClassesInfos()     獲取所有類的信息

publicstatic ClassItem GetClassInfo(string className, paramsobject[] parameters)    根據類名和類的構造函數參數獲取對應的類信息

類的信息顯示效果如下(左側顯示類的信息,右側編輯器顯示類的基本結構):

(2)TreeDrawer主要用於繪制類的樹結構,根據不同的類結構顯示不同的效果。這裡是用Winform來顯示的,本來打算用Silverlight來實現,但是由於時間關系,將就著這樣算了。當然,Silverlight顯示的效果比Winform強多了,而且,本人Silverlight水平比Winform熟練很多(以前項目中用Silverlight動態繪制相關圖形,因此比較熟悉)....

TreeDrawer的主要方法為以下2個:

public Bitmap CreateImage(ClassItem classItem, Font font)
        {
            if (classItem == null || font == null)
            {
                return null;
            }
    
            ClassBlock classBlock = CreateCurrentClassBlock(classItem);
            AddLinesAndBlockTexts(classBlock, font);
    
            Bitmap bitmap = new Bitmap(3 * BLOCK_WIDTH + 2 * BLOCK_INNER_WIDTH, this.Height);
            Graphics graphics = Graphics.FromImage(bitmap);
            graphics.Clear(Color.White);
            Pen ellipsePen = new Pen(Color.Blue, 2);
    
            foreach (Line line in this.Lines)
            {
                graphics.DrawLine(ellipsePen, line.X1, line.Y1, line.X2, line.Y2);
                if (line.HasArrow)
                {
                    PointF[] points = CreateArrowPoints(new PointF(line.X1, line.Y1),
                        new PointF(line.X2, line.Y2), ARROW_LENGTH, RELATIVE_VALUE);
                    DrawArrowHead(graphics, points);
                }
            }
    
            foreach (BlockText content in this.Contents)
            {
                graphics.DrawString(content.Content, font, Brushes.Black, content.StartX, content.StartY);
            }
    
            return bitmap;
        }
    
        private ClassBlock CreateCurrentClassBlock(ClassItem classItem)
        {
            int originalParamY = 0;
            int lastParamY = 0;
            int currentParamY = 0;
            int currentMethodY = 0;
            int startMethodX = BLOCK_WIDTH + BLOCK_INNER_WIDTH;         
            int startParamX = 2 * BLOCK_WIDTH + 2 * BLOCK_INNER_WIDTH;
    
            List<MethodBlock> methodBlocks = new List<MethodBlock>();
            foreach (MethodItem methodItem in classItem.Methods)
            {
                int paramsCount = methodItem.Parameters.Count;
                if (paramsCount > 0)
                {
                    lastParamY += paramsCount * (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT);
                }
                else
                {
                    lastParamY += (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT);
                    currentParamY += (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT);
                }
    
                currentMethodY = ((lastParamY - BLOCK_INNER_HEGIHT - originalParamY) / 2 + originalParamY - (BLOCK_HEIGHT / 2));
                originalParamY = lastParamY;
    
                MethodBlock methodBlock = new MethodBlock(new Point(startMethodX, currentMethodY), BLOCK_WIDTH, BLOCK_HEIGHT);
                methodBlock.Content = methodItem.Name;
                methodBlocks.Add(methodBlock);
                foreach (ParameterItem parameterItem in methodItem.Parameters)
                {
                    ParameterBlock parameterBlock = new ParameterBlock(new Point(startParamX, currentParamY), BLOCK_WIDTH, BLOCK_HEIGHT);
                    parameterBlock.Content = parameterItem.DefaultName;
                    methodBlock.ParameterBlocks.Add(parameterBlock);
                    currentParamY += (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT);
                }
            }
    
            if (lastParamY > 0)
            {
                lastParamY -= BLOCK_INNER_HEGIHT;
            }
            else
            {
                lastParamY = BLOCK_HEIGHT;
            }
    
            this.Height = lastParamY;
    
            Point classStartPoint = new Point(0, 0);
            if (classItem.Methods.Count > 1)
            {
                int y = (methodBlocks.Last().LeftBottomPoint.Y - methodBlocks.First().LeftBottomPoint.Y) / 2;
                classStartPoint = new Point(0, y);
            }
    
            ClassBlock classBlock = new ClassBlock(classStartPoint, BLOCK_WIDTH, BLOCK_HEIGHT);
            classBlock.Content = classItem.Name;
            foreach (MethodBlock methodBlock in methodBlocks)
            {
                classBlock.MethodBlocks.Add(methodBlock);
            }
    
            return classBlock;
        }

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類樹結構源碼

http://files.cnblogs.com/jasenkin/IronRuby/Jasen.IronRubyApp.rar

作者:JasenKin

出處:http://www.cnblogs.com/jasenkin/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved